From b4133fcf96eeb591e92303f6c65e1ec0a1f6ee8e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 31 Jan 2023 18:24:12 +0100 Subject: [PATCH 001/201] Remove irrelevant asserts --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index cebef8b42..663aafc77 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -576,9 +576,6 @@ bool GLGizmoEmboss::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.Moving()) return false; // not selected volume - assert(m_volume != nullptr); - assert(priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) != nullptr); - assert(m_volume->text_configuration.has_value()); if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || !m_volume->text_configuration.has_value()) return false; From 617c163bd634580e27c85fe49445f408d6105470 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 31 Jan 2023 19:23:09 +0100 Subject: [PATCH 002/201] Write result transformation into all gl_volumes --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 663aafc77..32dc65e9b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -547,11 +547,25 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // with Mesa driver OR on Linux if (!m_temp_transformation.has_value()) return false; + int instance_idx = m_parent.get_selection().get_instance_idx(); + const auto &instances = m_volume->get_object()->instances; + if (instance_idx < 0 || instance_idx >= instances.size()) + return false; + // Override of common transformation after draggig by set transformation into gl_volume - Transform3d volume_trmat = - gl_volume->get_instance_transformation().get_matrix().inverse() * + Transform3d volume_trmat = + instances[instance_idx]->get_matrix().inverse() * *m_temp_transformation; - gl_volume->set_volume_transformation(Geometry::Transformation(volume_trmat)); + + // ReWrite transformation inside of all instances + Geometry::Transformation transformation(volume_trmat); + for (GLVolume *vol : m_parent.get_volumes().volumes) { + if (vol->object_idx() != gl_volume->object_idx() || + vol->volume_idx() != gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(transformation); + } + m_parent.toggle_model_objects_visibility(true); // Apply temporary position m_temp_transformation = {}; From baa5a011ee6e085a3323c72b4db4652b10a91c15 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 11:21:41 +0100 Subject: [PATCH 003/201] Rework dragging over surface to use GlVolume and function do_move No need for special rendering any more --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 256 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 23 ++- 2 files changed, 122 insertions(+), 157 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 32dc65e9b..4bc5d22ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -466,70 +466,77 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { - // filter events - if (!(mouse_event.Dragging() && mouse_event.LeftIsDown()) && - !mouse_event.LeftUp() && - !mouse_event.LeftDown()) - return false; - - // must exist hover object - int hovered_id = m_parent.get_first_hover_volume_idx(); - if (hovered_id < 0) return false; - - GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id]; - const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects; - ModelVolume *act_model_volume = priv::get_model_volume(gl_volume, objects); - - // hovered object must be actual text volume - if (m_volume != act_model_volume) return false; - - const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; - std::vector allowed_volumes_id; - if (volumes.size() > 1) { - allowed_volumes_id.reserve(volumes.size() - 1); - for (auto &v : volumes) { - if (v->id() == m_volume->id()) continue; - if (!v->is_model_part()) continue; - allowed_volumes_id.emplace_back(v->id().id); - } - } - - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - // detect start text dragging if (mouse_event.LeftDown()) { + // must exist hover object + int hovered_id = m_parent.get_first_hover_volume_idx(); + if (hovered_id < 0) + return false; + + GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id]; + const ModelObjectPtrs &objects = m_parent.get_model()->objects; + + // hovered object must be actual text volume + if (m_volume != priv::get_model_volume(gl_volume, objects)) + return false; + + const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; + std::vector allowed_volumes_id; + if (volumes.size() > 1) { + allowed_volumes_id.reserve(volumes.size() - 1); + for (auto &v : volumes) { + if (v->id() == m_volume->id()) + continue; + if (!v->is_model_part()) + continue; + allowed_volumes_id.emplace_back(v->id().id); + } + } + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + // initialize raycasters - // IMPROVE: move to job, for big scene it slows down - ModelObject *act_model_object = act_model_volume->get_object(); - m_raycast_manager.actualize(act_model_object, &condition); - m_dragging_mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); + // INFO: It could slows down for big objects + // (may be move to thread and do not show drag until it finish) + m_raycast_manager.actualize(m_volume->get_object(), &condition); + + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); + Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); + m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, gl_volume, condition}; + // Cancel job to prevent interuption of dragging (duplicit result) if (m_job_cancel != nullptr) m_job_cancel->store(true); - return false; + + m_parent.enable_moving(false); + m_parent.enable_picking(false); + return true; } // Dragging starts out of window - if (!m_dragging_mouse_offset.has_value()) + if (!m_surface_drag.has_value()) return false; if (mouse_event.Dragging()) { + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - Vec2d offseted_mouse = mouse_pos + *m_dragging_mouse_offset; - auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &condition); - if (!hit.has_value()) - return false; - TextConfiguration &tc = *m_volume->text_configuration; - // INFO: GLVolume is transformed by common movement but we need move over surface - // so hide common dragging of object - m_parent.toggle_model_objects_visibility(false, m_volume->get_object(), gl_volume->instance_idx(), m_volume); + auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + if (!hit.has_value()) { + // cross hair need redraw + m_parent.set_as_dirty(); + return true; + } // Calculate temporary position Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); + + TextConfiguration &tc = *m_volume->text_configuration; const FontProp& font_prop = tc.style.prop; apply_transformation(font_prop, trmat); @@ -537,49 +544,43 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (tc.fix_3mf_tr.has_value()) trmat = trmat * (*tc.fix_3mf_tr); - // temp is in world coors - m_temp_transformation = object_trmat * trmat; + // volume transfomration in world coor + Transform3d world = object_trmat * trmat; + Transform3d volume_tr = m_surface_drag->instance_inv * world; + + // Update transformation inside of instances + for (GLVolume *vol : m_parent.get_volumes().volumes) { + if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || + vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(volume_tr); + } // calculate scale calculate_scale(); + + m_parent.set_as_dirty(); + return true; } else if (mouse_event.LeftUp()) { - // Added because of weird case after double click into scene - // with Mesa driver OR on Linux - if (!m_temp_transformation.has_value()) return false; - - int instance_idx = m_parent.get_selection().get_instance_idx(); - const auto &instances = m_volume->get_object()->instances; - if (instance_idx < 0 || instance_idx >= instances.size()) - return false; - - // Override of common transformation after draggig by set transformation into gl_volume - Transform3d volume_trmat = - instances[instance_idx]->get_matrix().inverse() * - *m_temp_transformation; - - // ReWrite transformation inside of all instances - Geometry::Transformation transformation(volume_trmat); - for (GLVolume *vol : m_parent.get_volumes().volumes) { - if (vol->object_idx() != gl_volume->object_idx() || - vol->volume_idx() != gl_volume->volume_idx()) - continue; - vol->set_volume_transformation(transformation); - } - - m_parent.toggle_model_objects_visibility(true); - // Apply temporary position - m_temp_transformation = {}; - m_dragging_mouse_offset = {}; + // write transformation from UI into model + Selection &s = m_parent.get_selection(); + Selection::EMode mode = s.get_mode(); + s.set_mode(Selection::EMode::Volume); // Want to move with all volumes inside of instances + m_parent.do_move(L("Surface move")); + s.set_mode(mode); // revert setting of mode // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) { - // need actual position - m_volume->set_transformation(volume_trmat); + if (m_volume->text_configuration->style.prop.use_surface) process(); - } // calculate scale calculate_scale(); + + // allow moving and picking again + m_parent.enable_moving(true); + m_parent.enable_picking(true); + m_surface_drag.reset(); + return true; } return false; } @@ -619,53 +620,10 @@ void GLGizmoEmboss::on_render() { Selection &selection = m_parent.get_selection(); if (selection.is_empty()) return; - if (m_temp_transformation.has_value()) { - // draw text volume on temporary position - GLVolume& gl_volume = *selection.get_volume(*selection.get_volume_idxs().begin()); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - shader->start_using(); - - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d matrix = camera.get_view_matrix() * (*m_temp_transformation); - shader->set_uniform("view_model_matrix", matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_normal_matrix", (Matrix3d) (matrix).matrix().block(0, 0, 3, 3).inverse().transpose()); - shader->set_uniform("emission_factor", 0.0f); - - // dragging object must be selected so draw it with correct color - //auto color = gl_volume.color; - //auto color = gl_volume.render_color; - auto color = GLVolume::SELECTED_COLOR; - // Set transparent color for NEGATIVE_VOLUME & PARAMETER_MODIFIER - bool is_transparent = m_volume->type() != ModelVolumeType::MODEL_PART; - if (is_transparent) { - color.a(0.5f); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - } - - bool is_left_handed = has_reflection(*m_temp_transformation); - if (is_left_handed) - glsafe(::glFrontFace(GL_CW)); - - glsafe(::glEnable(GL_DEPTH_TEST)); - gl_volume.model.set_color(color); - gl_volume.model.render(); - glsafe(::glDisable(GL_DEPTH_TEST)); - - // set it back to pevious state - if (is_left_handed) - glsafe(::glFrontFace(GL_CCW)); - if (is_transparent) - glsafe(::glDisable(GL_BLEND)); - - shader->stop_using(); - } - // prevent get local coordinate system on multi volumes if (!selection.is_single_volume_or_modifier() && !selection.is_single_volume_instance()) return; - bool is_surface_dragging = m_temp_transformation.has_value(); + bool is_surface_dragging = m_surface_drag.has_value(); bool is_parent_dragging = m_parent.is_mouse_dragging(); // Do NOT render rotation grabbers when dragging object bool is_rotate_by_grabbers = m_dragging; @@ -737,36 +695,27 @@ static void draw_mouse_offset(const std::optional &offset) draw_list->AddLine(p1, p2, color, thickness); } #endif // SHOW_OFFSET_DURING_DRAGGING -namespace priv { -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); - Vec3d volume_zero = to_world * Vec3d::Zero(); - - 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 = 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); +namespace priv { +static void draw_cross_hair(const ImVec2 &position, + float radius = 16.f, + ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)), + int num_segments = 0, + float thickness = 4.f); +} // namespace priv + +void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) +{ + auto draw_list = ImGui::GetOverlayDrawList(); + draw_list->AddCircle(position, 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); + ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius); + ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius); draw_list->AddLine(start, end, color, thickness); } } -} // namespace priv - void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { if (!m_gui_cfg.has_value()) initialize(); @@ -784,8 +733,13 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, min_window_size); // Draw origin position of text during dragging - if (m_temp_transformation.has_value()) - priv::draw_origin(m_parent); + if (m_surface_drag.has_value()) { + ImVec2 mouse_pos = ImGui::GetMousePos(); + ImVec2 center( + mouse_pos.x + m_surface_drag->mouse_offset.x(), + mouse_pos.y + m_surface_drag->mouse_offset.y()); + priv::draw_cross_hair(center); + } #ifdef SHOW_FINE_POSITION draw_fine_position(m_parent.get_selection(), m_parent.get_canvas_size(), min_window_size); @@ -1288,9 +1242,7 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) } void GLGizmoEmboss::calculate_scale() { - Transform3d to_world = m_temp_transformation.has_value()? - *m_temp_transformation : - priv::world_matrix(m_parent.get_selection()); + Transform3d to_world = m_parent.get_selection().get_first_volume()->world_matrix(); auto to_world_linear = to_world.linear(); auto calc = [&to_world_linear](const Vec3d &axe, std::optional& scale)->bool { Vec3d axe_world = to_world_linear * axe; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 2ed6c2000..443d83d3b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -299,16 +299,29 @@ private: // Value is set only when dragging rotation to calculate actual angle std::optional m_rotate_start_angle; - // when draging with text object hold screen offset of cursor from object center - std::optional m_dragging_mouse_offset; + // Data for drag&drop over surface with mouse + struct SurfaceDrag + { + // hold screen coor offset of cursor from object center + Vec2d mouse_offset; + + // Invers transformation of text volume instance + // Help convert world transformation to instance space + Transform3d instance_inv; + + // Dragged gl volume + GLVolume *gl_volume; + + // condition for raycaster + RaycastManager::AllowVolumes condition; + }; + // Keep data about dragging only during drag&drop + std::optional m_surface_drag; // TODO: it should be accessible by other gizmo too. // May be move to plater? RaycastManager m_raycast_manager; - // Only when drag text object it stores world position - std::optional m_temp_transformation; - // For text on scaled objects std::optional m_scale_height; std::optional m_scale_depth; From a2fb0c377fd5750cac547bebb5ed690610901040 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 12:38:04 +0100 Subject: [PATCH 004/201] Fix unselecting of volume after drag --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 4bc5d22ed..8f7c3de01 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -510,8 +510,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_job_cancel != nullptr) m_job_cancel->store(true); + // disable moving with object by mouse m_parent.enable_moving(false); - m_parent.enable_picking(false); return true; } @@ -563,11 +563,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) return true; } else if (mouse_event.LeftUp()) { // write transformation from UI into model - Selection &s = m_parent.get_selection(); - Selection::EMode mode = s.get_mode(); - s.set_mode(Selection::EMode::Volume); // Want to move with all volumes inside of instances m_parent.do_move(L("Surface move")); - s.set_mode(mode); // revert setting of mode // Update surface by new position if (m_volume->text_configuration->style.prop.use_surface) @@ -576,9 +572,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // calculate scale calculate_scale(); - // allow moving and picking again + // allow moving with object again m_parent.enable_moving(true); - m_parent.enable_picking(true); m_surface_drag.reset(); return true; } From d7bd20b957b4f62e77735a562e54ae19fbd79110 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 15:59:39 +0100 Subject: [PATCH 005/201] Fix drag&drop outside of screen --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 40 +++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 8f7c3de01..5d8cf47bf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -466,6 +466,29 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { + auto do_move = [&]() { + // write transformation from UI into model + m_parent.do_move(L("Surface move")); + + // Update surface by new position + if (m_volume->text_configuration->style.prop.use_surface) + process(); + + // calculate scale + calculate_scale(); + + // allow moving with object again + m_parent.enable_moving(true); + m_surface_drag.reset(); + }; + + if (mouse_event.Moving()) { + // Fix when leave window during dragging and move cursor back + if (m_surface_drag.has_value()) + do_move(); + return false; + } + // detect start text dragging if (mouse_event.LeftDown()) { // must exist hover object @@ -562,19 +585,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.set_as_dirty(); return true; } else if (mouse_event.LeftUp()) { - // write transformation from UI into model - m_parent.do_move(L("Surface move")); - - // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) - process(); - - // calculate scale - calculate_scale(); - - // allow moving with object again - m_parent.enable_moving(true); - m_surface_drag.reset(); + do_move(); return true; } return false; @@ -582,9 +593,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) bool GLGizmoEmboss::on_mouse(const wxMouseEvent &mouse_event) { - // do not process moving event - if (mouse_event.Moving()) return false; - // not selected volume if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || From 7ef291d052f8979ab21440c8190af0c1484f3170 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 16:01:20 +0100 Subject: [PATCH 006/201] Fix gui transformation when change DPI - initialize m_gui_cfg only when start draw imgui window --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 100 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 10 ++- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 5d8cf47bf..1c8e5a28c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -223,9 +223,8 @@ bool priv::is_valid(ModelVolumeType volume_type){ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { if (!priv::is_valid(volume_type)) return; - if (!m_gui_cfg.has_value()) initialize(); - set_default_text(); m_style_manager.discard_style_changes(); + set_default_text(); GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); @@ -242,9 +241,8 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) { if (!priv::is_valid(volume_type)) return; - if (!m_gui_cfg.has_value()) initialize(); - set_default_text(); m_style_manager.discard_style_changes(); + set_default_text(); // select position by camera position and view direction const Selection &selection = m_parent.get_selection(); @@ -721,7 +719,14 @@ void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, in void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_gui_cfg.has_value()) initialize(); + // Check that DPI is same + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + if (m_gui_cfg.has_value() && m_gui_cfg->screen_scale != screen_scale) + m_gui_cfg.reset(); + + // Cache for gui offsets + if (!m_gui_cfg.has_value()) + initialize(screen_scale); set_volume_by_selection(); // Do not render window for not selected text volume @@ -840,8 +845,6 @@ void GLGizmoEmboss::on_set_state() m_style_manager.store_styles_to_app_config(false); remove_notification_not_valid_font(); } else if (GLGizmoBase::m_state == GLGizmoBase::On) { - if (!m_gui_cfg.has_value()) initialize(); - // to reload fonts from system, when install new one wxFontEnumerator::InvalidateCache(); @@ -861,10 +864,15 @@ void GLGizmoEmboss::on_set_state() } // change position of just opened emboss window - if (m_allow_open_near_volume) + if (m_allow_open_near_volume) { m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); - else - priv::change_window_position(m_set_window_offset, false); + } else { + if (m_gui_cfg.has_value()) + priv::change_window_position(m_set_window_offset, false); + else + m_set_window_offset = ImVec2(-1, -1); + } + // when open by hyperlink it needs to show up // or after key 'T' windows doesn't appear m_parent.set_as_dirty(); @@ -896,11 +904,13 @@ void GLGizmoEmboss::on_stop_dragging() } void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } -void GLGizmoEmboss::initialize() +void GLGizmoEmboss::initialize(double screen_scale) { if (m_gui_cfg.has_value()) return; - GuiCfg cfg; // initialize by default values; + GuiCfg cfg; // initialize by default values; + cfg.screen_scale = screen_scale; + float line_height = ImGui::GetTextLineHeight(); float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); float space = line_height_with_spacing - line_height; @@ -1147,8 +1157,9 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) // search in enumerated fonts // refresh list of installed font in the OS. - init_face_names(); + init_face_names(m_face_names); m_face_names.is_init = false; + auto cmp = [](const FaceName &fn, const wxString& face_name)->bool { return fn.wx_name < face_name; }; const std::vector &faces = m_face_names.faces; auto it = std::lower_bound(faces.begin(), faces.end(), face_name, cmp); @@ -1779,31 +1790,34 @@ bool GLGizmoEmboss::load(Facenames &facenames) { return true; } -void GLGizmoEmboss::init_face_names() { +void GLGizmoEmboss::init_truncated_names(Facenames &face_names, float max_width) +{ + for (FaceName &face : face_names.faces) { + std::string name_str(face.wx_name.ToUTF8().data()); + face.name_truncated = ImGuiWrapper::trunc(name_str, max_width); + } + face_names.has_truncated_names = true; +} + +void GLGizmoEmboss::init_face_names(Facenames &face_names) +{ Timer t("enumerate_fonts"); - if (m_face_names.is_init) return; - m_face_names.is_init = true; + if (face_names.is_init) return; + face_names.is_init = true; // to reload fonts from system, when install new one wxFontEnumerator::InvalidateCache(); - auto create_truncated_names = [&facenames = m_face_names, &width = m_gui_cfg->face_name_max_width]() { - for (FaceName &face : facenames.faces) { - std::string name_str(face.wx_name.ToUTF8().data()); - face.name_truncated = ImGuiWrapper::trunc(name_str, width); - } - }; - // try load cache // Only not OS enumerated face has hash value 0 - if (m_face_names.hash == 0) { - load(m_face_names); - create_truncated_names(); + if (face_names.hash == 0) { + load(face_names); + face_names.has_truncated_names = false; } using namespace std::chrono; steady_clock::time_point enumerate_start = steady_clock::now(); - ScopeGuard sg([&enumerate_start, &face_names = m_face_names]() { + ScopeGuard sg([&enumerate_start, &face_names = face_names]() { steady_clock::time_point enumerate_end = steady_clock::now(); long long enumerate_duration = duration_cast(enumerate_end - enumerate_start).count(); BOOST_LOG_TRIVIAL(info) << "OS enumerate " << face_names.faces.size() << " fonts " @@ -1811,25 +1825,25 @@ void GLGizmoEmboss::init_face_names() { << "= " << face_names.faces.size() + face_names.bad.size() << " fonts) " << "in " << enumerate_duration << " ms\n" << concat(face_names.bad); }); - wxArrayString facenames = wxFontEnumerator::GetFacenames(m_face_names.encoding); + wxArrayString facenames = wxFontEnumerator::GetFacenames(face_names.encoding); size_t hash = boost::hash_range(facenames.begin(), facenames.end()); // Zero value is used as uninitialized hash if (hash == 0) hash = 1; // check if it is same as last time - if (m_face_names.hash == hash) { + if (face_names.hash == hash) { // no new installed font BOOST_LOG_TRIVIAL(info) << "Same FontNames hash, cache is used. " << "For clear cache delete file: " << get_fontlist_cache_path().string(); return; } - BOOST_LOG_TRIVIAL(info) << ((m_face_names.hash == 0) ? + BOOST_LOG_TRIVIAL(info) << ((face_names.hash == 0) ? "FontName list is generate from scratch." : "Hash are different. Only previous bad fonts are used and set again as bad"); - m_face_names.hash = hash; + face_names.hash = hash; // validation lambda - auto is_valid_font = [encoding = m_face_names.encoding, bad = m_face_names.bad /*copy*/](const wxString &name) { + auto is_valid_font = [encoding = face_names.encoding, bad = face_names.bad /*copy*/](const wxString &name) { if (name.empty()) return false; // vertical font start with @, we will filter it out @@ -1856,20 +1870,20 @@ void GLGizmoEmboss::init_face_names() { return true; }; - m_face_names.faces.clear(); - m_face_names.bad.clear(); - m_face_names.faces.reserve(facenames.size()); + face_names.faces.clear(); + face_names.bad.clear(); + face_names.faces.reserve(facenames.size()); std::sort(facenames.begin(), facenames.end()); for (const wxString &name : facenames) { if (is_valid_font(name)) { - m_face_names.faces.push_back({name}); + face_names.faces.push_back({name}); }else{ - m_face_names.bad.push_back(name); + face_names.bad.push_back(name); } } - assert(std::is_sorted(m_face_names.bad.begin(), m_face_names.bad.end())); - create_truncated_names(); - store(m_face_names); + assert(std::is_sorted(face_names.bad.begin(), face_names.bad.end())); + face_names.has_truncated_names = false; + store(face_names); } // create texture for visualization font face @@ -2073,9 +2087,13 @@ void GLGizmoEmboss::draw_font_list() { bool set_selection_focus = false; if (!m_face_names.is_init) { - init_face_names(); + init_face_names(m_face_names); set_selection_focus = true; } + + if (!m_face_names.has_truncated_names) + init_truncated_names(m_face_names, m_gui_cfg->face_name_max_width); + if (m_face_names.texture_id == 0) init_font_name_texture(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 443d83d3b..50bec2a28 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -82,7 +82,7 @@ protected: std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); } std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); } private: - void initialize(); + void initialize(double screen_scale); static EmbossStyles create_default_styles(); // localized default text void set_default_text(); @@ -118,7 +118,6 @@ private: void draw_advanced(); bool select_facename(const wxString& facename); - void init_face_names(); void do_translate(const Vec3d& relative_move); void do_rotate(float relative_z_angle); @@ -167,6 +166,9 @@ private: // so the change takes effect. (info by GLGizmoFdmSupports.hpp) struct GuiCfg { + // Detect invalid config values when change monitor DPI + double screen_scale; + // Zero means it is calculated in init function ImVec2 minimal_window_size = ImVec2(0, 0); ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); @@ -244,6 +246,8 @@ private: // true .. already enumerated(During opened combo box) bool is_init = false; + bool has_truncated_names = false; + // data of can_load() faces std::vector faces = {}; // Sorter set of Non valid face names in OS @@ -277,6 +281,8 @@ private: static bool store(const Facenames &facenames); static bool load(Facenames &facenames); + static void init_face_names(Facenames &facenames); + static void init_truncated_names(Facenames &face_names, float max_width); // Text to emboss std::string m_text; From f9b39c0f6f76e57e2e2f88ace3058c244581cc47 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 17:56:56 +0100 Subject: [PATCH 007/201] Re-Initialize Emboss GUI configuration on change monitor scale --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 46 ++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 5 ++- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 1c8e5a28c..41ee66109 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -608,6 +608,12 @@ bool GLGizmoEmboss::on_init() ColorRGBA gray_color(.6f, .6f, .6f, .3f); m_rotate_gizmo.set_highlight_color(gray_color); m_shortcut_key = WXK_CONTROL_T; + + // initialize text styles + m_style_manager.init(wxGetApp().app_config); + + // Set rotation gizmo upwardrotate + m_rotate_gizmo.set_angle(PI / 2); return true; } @@ -720,13 +726,20 @@ void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, in void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { // Check that DPI is same - double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); - if (m_gui_cfg.has_value() && m_gui_cfg->screen_scale != screen_scale) - m_gui_cfg.reset(); + GUI_App& app = wxGetApp(); + double screen_scale = wxDisplay(app.plater()).GetScaleFactor(); + if (!m_gui_cfg.has_value() || + m_gui_cfg->screen_scale != screen_scale) { + // Create cache for gui offsets + GuiCfg cfg = create_gui_configuration(); + cfg.screen_scale = screen_scale; + m_gui_cfg.emplace(std::move(cfg)); + // set position near toolbar + m_set_window_offset = ImVec2(-1.f, -1.f); - // Cache for gui offsets - if (!m_gui_cfg.has_value()) - initialize(screen_scale); + // change resolution regenerate icons + init_icons(); + } set_volume_by_selection(); // Do not render window for not selected text volume @@ -904,12 +917,9 @@ void GLGizmoEmboss::on_stop_dragging() } void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } -void GLGizmoEmboss::initialize(double screen_scale) +GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() { - if (m_gui_cfg.has_value()) return; - - GuiCfg cfg; // initialize by default values; - cfg.screen_scale = screen_scale; + GuiCfg cfg; // initialize by default values; float line_height = ImGui::GetTextLineHeight(); float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); @@ -996,17 +1006,7 @@ void GLGizmoEmboss::initialize(double screen_scale) int max_style_image_height = 1.5 * input_height; cfg.max_style_image_size = Vec2i(max_style_image_width, max_style_image_height); cfg.face_name_size.y() = line_height_with_spacing; - - m_gui_cfg.emplace(std::move(cfg)); - - init_icons(); - - // initialize text styles - m_style_manager.init(wxGetApp().app_config); - set_default_text(); - - // Set rotation gizmo upwardrotate - m_rotate_gizmo.set_angle(PI/2); + return cfg; } EmbossStyles GLGizmoEmboss::create_default_styles() @@ -1014,7 +1014,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles() wxFont wx_font_normal = *wxNORMAL_FONT; wxFont wx_font_small = *wxSMALL_FONT; -#ifdef __APPLE__ +#ifdef __APPLE__= wx_font_normal.SetFaceName("Helvetica"); wx_font_small.SetFaceName("Helvetica"); #endif // __APPLE__ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 50bec2a28..77b86b8b2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -82,7 +82,6 @@ protected: std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); } std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); } private: - void initialize(double screen_scale); static EmbossStyles create_default_styles(); // localized default text void set_default_text(); @@ -213,10 +212,10 @@ private: std::string collection; }; Translations translations; - - GuiCfg() = default; }; std::optional m_gui_cfg; + static GuiCfg create_gui_configuration(); + bool m_is_advanced_edit_style = false; // when true window will appear near to text volume when open From 5cc1e4dac0c798ab87f4d93f004c07f3d602dc03 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 07:56:38 +0100 Subject: [PATCH 008/201] add missing include --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 41ee66109..2876d438c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -34,6 +34,7 @@ #include #include #include +#include // detection of change DPI #include From 21ff6bc048454c1075f3aa4eb74688e99d6e1c3a Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 09:42:30 +0100 Subject: [PATCH 009/201] Fix emboss window position when change slicer size --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 65 ++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 2 + 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 2876d438c..95736e271 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -726,23 +726,7 @@ void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, in void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { - // Check that DPI is same - GUI_App& app = wxGetApp(); - double screen_scale = wxDisplay(app.plater()).GetScaleFactor(); - if (!m_gui_cfg.has_value() || - m_gui_cfg->screen_scale != screen_scale) { - // Create cache for gui offsets - GuiCfg cfg = create_gui_configuration(); - cfg.screen_scale = screen_scale; - m_gui_cfg.emplace(std::move(cfg)); - // set position near toolbar - m_set_window_offset = ImVec2(-1.f, -1.f); - - // change resolution regenerate icons - init_icons(); - } set_volume_by_selection(); - // Do not render window for not selected text volume if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || @@ -751,6 +735,26 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) return; } + // Configuration creation + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + float main_toolbar_height = m_parent.get_main_toolbar_height(); + if (!m_gui_cfg.has_value() || // Exist configuration - first run + m_gui_cfg->screen_scale != screen_scale || // change of DPI + m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port + ) { + // Create cache for gui offsets + GuiCfg cfg = create_gui_configuration(); + cfg.screen_scale = screen_scale; + cfg.main_toolbar_height = main_toolbar_height; + m_gui_cfg.emplace(std::move(cfg)); + // set position near toolbar + m_set_window_offset = ImVec2(-1.f, -1.f); + + // change resolution regenerate icons + init_icons(); + m_style_manager.clear_imgui_font(); + } + const ImVec2 &min_window_size = get_minimal_window_size(); ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, min_window_size); @@ -925,10 +929,11 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() float line_height = ImGui::GetTextLineHeight(); float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); float space = line_height_with_spacing - line_height; + const ImGuiStyle &style = ImGui::GetStyle(); cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x; - cfg.icon_width = std::ceil(line_height); + cfg.icon_width = static_cast(std::ceil(line_height)); // make size pair number if (cfg.icon_width % 2 != 0) ++cfg.icon_width; @@ -946,8 +951,8 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() ImGui::CalcTextSize(tr.font.c_str()).x, ImGui::CalcTextSize(tr.size.c_str()).x, ImGui::CalcTextSize(tr.depth.c_str()).x}); - cfg.input_offset = max_text_width - + 3 * space + ImGui::GetTreeNodeToLabelSpacing(); + cfg.indent = static_cast(cfg.icon_width); + cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space; tr.use_surface = _u8L("Use surface"); tr.char_gap = _u8L("Char gap"); @@ -967,10 +972,9 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() ImGui::CalcTextSize(tr.angle.c_str()).x, ImGui::CalcTextSize(tr.collection.c_str()).x }); cfg.advanced_input_offset = max_advanced_text_width - + 3 * space + ImGui::GetTreeNodeToLabelSpacing(); + + 3 * space + cfg.indent; // calculate window size - const ImGuiStyle &style = ImGui::GetStyle(); float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y; float input_height = line_height_with_spacing + 2*style.FramePadding.y; float tree_header = line_height_with_spacing; @@ -1495,12 +1499,13 @@ void GLGizmoEmboss::draw_window() ScopeGuard unknown_font_sc([&]() { m_imgui->disabled_end(); }); - draw_text_input(); m_imgui->disabled_begin(!is_active_font); - ImGui::TreePush(); + + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); + ImGui::Indent(); draw_style_edit(); - ImGui::TreePop(); + ImGui::Unindent(); // close advanced style property when unknown font is selected if (m_is_unknown_font && m_is_advanced_edit_style) @@ -1516,6 +1521,8 @@ void GLGizmoEmboss::draw_window() } else if (m_is_advanced_edit_style) set_minimal_window_size(false); + ImGui::PopStyleVar(); // ImGuiStyleVar_IndentSpacing + ImGui::Separator(); draw_style_list(); @@ -1590,7 +1597,9 @@ void GLGizmoEmboss::draw_text_input() ImFont *imgui_font = m_style_manager.get_imgui_font(); if (imgui_font == nullptr) { // try create new imgui font - m_style_manager.create_imgui_font(create_range_text_prep(), scale); + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + double imgui_scale = scale * screen_scale; + m_style_manager.create_imgui_font(create_range_text_prep(), imgui_scale); imgui_font = m_style_manager.get_imgui_font(); } bool exist_font = @@ -1680,10 +1689,8 @@ void GLGizmoEmboss::draw_text_input() // IMPROVE: only extend not clear // Extend font ranges if (!range_text.empty() && - !m_imgui->contain_all_glyphs(imgui_font, range_text) ) { - m_style_manager.clear_imgui_font(); - m_style_manager.create_imgui_font(range_text, scale); - } + !m_imgui->contain_all_glyphs(imgui_font, range_text) ) + m_style_manager.clear_imgui_font(); } #include diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 77b86b8b2..0cfe34508 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -167,6 +167,7 @@ private: { // Detect invalid config values when change monitor DPI double screen_scale; + float main_toolbar_height; // Zero means it is calculated in init function ImVec2 minimal_window_size = ImVec2(0, 0); @@ -181,6 +182,7 @@ private: // maximal width and height of style image Vec2i max_style_image_size = Vec2i(0, 0); + float indent = 0.f; float input_offset = 0.f; float advanced_input_offset = 0.f; From d5686a0ecc18cf3acf495dff8649bda38042afb7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 09:50:30 +0100 Subject: [PATCH 010/201] Fix typo --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 95736e271..d72fb5dc2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1019,7 +1019,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles() wxFont wx_font_normal = *wxNORMAL_FONT; wxFont wx_font_small = *wxSMALL_FONT; -#ifdef __APPLE__= +#ifdef __APPLE__ wx_font_normal.SetFaceName("Helvetica"); wx_font_small.SetFaceName("Helvetica"); #endif // __APPLE__ From 4b31db5e66af4fc525869b80ad6641a5820b6511 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 12:36:01 +0100 Subject: [PATCH 011/201] Fix search in font list --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 77 +++++++++++++------------ src/slic3r/GUI/ImGuiWrapper.cpp | 4 ++ src/slic3r/GUI/ImGuiWrapper.hpp | 6 ++ 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index d72fb5dc2..3a7841f96 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1080,7 +1080,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles() void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } -#include "imgui/imgui_internal.h" // to unfocus input --> ClearActiveID void GLGizmoEmboss::set_volume_by_selection() { ModelVolume *vol = priv::get_selected_volume(m_parent.get_selection()); @@ -1094,47 +1093,12 @@ void GLGizmoEmboss::set_volume_by_selection() // Do not use focused input value when switch volume(it must swith value) if (m_volume != nullptr && m_volume != vol) // when update volume it changed id BUT not pointer - ImGui::ClearActiveID(); + ImGuiWrapper::left_inputs(); // is select embossed volume? set_volume(vol); } -// Need internals to get window -void priv::change_window_position(std::optional& output_window_offset, bool try_to_fix) { - const char* name = "Emboss"; - ImGuiWindow *window = ImGui::FindWindowByName(name); - // is window just created - if (window == NULL) - return; - - // position of window on screen - ImVec2 position = window->Pos; - ImVec2 size = window->SizeFull; - - // screen size - ImVec2 screen = ImGui::GetMainViewport()->Size; - - if (position.x < 0) { - if (position.y < 0) - output_window_offset = ImVec2(0, 0); - else - output_window_offset = ImVec2(0, position.y); - } else if (position.y < 0) { - output_window_offset = ImVec2(position.x, 0); - } else if (screen.x < (position.x + size.x)) { - if (screen.y < (position.y + size.y)) - output_window_offset = ImVec2(screen.x - size.x, screen.y - size.y); - else - output_window_offset = ImVec2(screen.x - size.x, position.y); - } else if (screen.y < (position.y + size.y)) { - output_window_offset = ImVec2(position.x, screen.y - size.y); - } - - if (!try_to_fix && output_window_offset.has_value()) - output_window_offset = ImVec2(-1, -1); // Cannot -} - bool GLGizmoEmboss::set_volume(ModelVolume *volume) { if (volume == nullptr) { @@ -2152,6 +2116,9 @@ void GLGizmoEmboss::draw_font_list() face.cancel->store(true); glsafe(::glDeleteTextures(1, &m_face_names.texture_id)); m_face_names.texture_id = 0; + + // Remove value from search input + ImGuiWrapper::left_inputs(); } // delete unloadable face name when try to use @@ -3923,5 +3890,41 @@ void priv::find_closest_volume(const Selection &selection, } } +// Need internals to get window +#include "imgui/imgui_internal.h" +void priv::change_window_position(std::optional& output_window_offset, bool try_to_fix) { + const char* name = "Emboss"; + ImGuiWindow *window = ImGui::FindWindowByName(name); + // is window just created + if (window == NULL) + return; + + // position of window on screen + ImVec2 position = window->Pos; + ImVec2 size = window->SizeFull; + + // screen size + ImVec2 screen = ImGui::GetMainViewport()->Size; + + if (position.x < 0) { + if (position.y < 0) + output_window_offset = ImVec2(0, 0); + else + output_window_offset = ImVec2(0, position.y); + } else if (position.y < 0) { + output_window_offset = ImVec2(position.x, 0); + } else if (screen.x < (position.x + size.x)) { + if (screen.y < (position.y + size.y)) + output_window_offset = ImVec2(screen.x - size.x, screen.y - size.y); + else + output_window_offset = ImVec2(screen.x - size.x, position.y); + } else if (screen.y < (position.y + size.y)) { + output_window_offset = ImVec2(position.x, screen.y - size.y); + } + + if (!try_to_fix && output_window_offset.has_value()) + output_window_offset = ImVec2(-1, -1); // Cannot +} + // any existing icon filename to not influence GUI const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg"; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 20913f66d..8bdecc5ef 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1360,6 +1360,10 @@ bool ImGuiWrapper::slider_optional_int(const char *label, } else return false; } +void ImGuiWrapper::left_inputs() { + ImGui::ClearActiveID(); +} + std::string ImGuiWrapper::trunc(const std::string &text, float width, const char * tail) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 3365954ba..d088dad2f 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -146,6 +146,12 @@ public: // Extended function ImGuiWrapper::slider_float to work with std::optional, when value == def_val than optional release its value bool slider_optional_int(const char* label, std::optional &v, int v_min, int v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, int def_val = 0); + /// + /// Use ImGui internals to unactivate (lose focus) in input. + /// When input is activ it can't change value by application. + /// + static void left_inputs(); + /// /// Truncate text by ImGui draw function to specific width /// NOTE 1: ImGui must be initialized From 66be5faedd8d609a285924be2d533c22c8374746 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 14:42:29 +0100 Subject: [PATCH 012/201] Move text only above selected instance. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 12 +++- src/slic3r/Utils/RaycastManager.cpp | 93 +++++++++++++++++++------ src/slic3r/Utils/RaycastManager.hpp | 1 + 3 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 3a7841f96..acf29d653 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -490,6 +490,10 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // detect start text dragging if (mouse_event.LeftDown()) { + // exist selected volume? + if (m_volume == nullptr) + return false; + // must exist hover object int hovered_id = m_parent.get_first_hover_volume_idx(); if (hovered_id < 0) @@ -502,6 +506,12 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume != priv::get_model_volume(gl_volume, objects)) return false; + const ModelInstancePtrs instances = m_volume->get_object()->instances; + int instance_id = gl_volume->instance_idx(); + if (instance_id < 0 || static_cast(instance_id) >= instances.size()) + return false; // should not happen + const ModelInstance *instance = instances[instance_id]; + const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; std::vector allowed_volumes_id; if (volumes.size() > 1) { @@ -519,7 +529,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(m_volume->get_object(), &condition); + m_raycast_manager.actualize(instance, &condition); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 4132fd647..12fc3dfed 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -6,34 +6,53 @@ #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Camera.hpp" -using namespace Slic3r::GUI; +using namespace Slic3r::GUI; -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +namespace priv { + +using namespace Slic3r; +// copied from private part of RaycastManager.hpp +using Raycaster = std::pair >; +// ModelVolume.id + +using Raycasters = std::vector; + +static void actualize(Raycasters &casters, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) { // check if volume was removed - std::vector removed_casters(m_raycasters.size(), {true}); - // check if inscance was removed - std::vector removed_transf(m_transformations.size(), {true}); - + std::vector removed_casters(casters.size(), {true}); // actualize MeshRaycaster - for (const ModelVolume *volume : object->volumes) { + for (const ModelVolume *volume : volumes) { size_t oid = volume->id().id; - if (skip != nullptr && skip->skip(oid)) + if (skip != nullptr && skip->skip(oid)) continue; - auto item = std::find_if(m_raycasters.begin(), m_raycasters.end(), - [oid](const RaycastManager::Raycaster &it)->bool { - return oid == it.first; - }); - if (item == m_raycasters.end()) { + auto item = std::find_if(casters.begin(), casters.end(), + [oid](const Raycaster &it) -> bool { return oid == it.first; }); + if (item == casters.end()) { // add new raycaster auto raycaster = std::make_unique(volume->get_mesh_shared_ptr()); - m_raycasters.emplace_back(std::make_pair(oid, std::move(raycaster))); + casters.emplace_back(std::make_pair(oid, std::move(raycaster))); } else { - size_t index = item - m_raycasters.begin(); + size_t index = item - casters.begin(); removed_casters[index] = false; } } + + // clean other raycasters + for (int i = removed_casters.size() - 1; i >= 0; --i) + if (removed_casters[i]) + casters.erase(casters.begin() + i); +} +} +void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +{ + // actualize MeshRaycaster + priv::actualize(m_raycasters, object->volumes, skip); + + // check if inscance was removed + std::vector removed_transf(m_transformations.size(), {true}); + // actualize transformation matrices for (const ModelVolume *volume : object->volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; @@ -41,8 +60,6 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) for (const ModelInstance *instance : object->instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - // TODO: add SLA shift Z - // transformation.translation()(2) += m_sla_shift_z; TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); auto item = std::find_if(m_transformations.begin(), m_transformations.end(), @@ -62,17 +79,49 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) } } - // clean other raycasters - for (int i = removed_casters.size() - 1; i >= 0; --i) - if (removed_casters[i]) - m_raycasters.erase(m_raycasters.begin() + i); - // clean other transformation for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); } +void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { + const ModelVolumePtrs &volumes = instance->get_object()->volumes; + + // actualize MeshRaycaster + priv::actualize(m_raycasters, volumes, skip); + + // check if inscance was removed + std::vector removed_transf(m_transformations.size(), {true}); + + // actualize transformation matrices + for (const ModelVolume *volume : volumes) { + if (skip != nullptr && skip->skip(volume->id().id)) + continue; + const Transform3d &volume_tr = volume->get_matrix(); + const Transform3d &instrance_tr = instance->get_matrix(); + Transform3d transformation = instrance_tr * volume_tr; + TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); + auto item = std::find_if(m_transformations.begin(), m_transformations.end(), + [&tr_key](const TrItem &it) -> bool { return it.first == tr_key; }); + if (item != m_transformations.end()) { + // actualize transformation all the time + item->second = transformation; + size_t index = item - m_transformations.begin(); + removed_transf[index] = false; + } else { + // add new transformation + m_transformations.emplace_back(std::make_pair(tr_key, transformation)); + } + } + + // clean other transformation + for (int i = removed_transf.size() - 1; i >= 0; --i) + if (removed_transf[i]) + m_transformations.erase(m_transformations.begin() + i); +} + + std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index a185f6307..94da384d2 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -51,6 +51,7 @@ public: /// Model representation /// Condifiton for skip actualization void actualize(const ModelObject *object, const ISkip *skip = nullptr); + void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); // TODO: it is more general object move outside of this class struct SurfacePoint From d63c954247d3552425fbe17ad13c53983d467532 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 3 Feb 2023 07:51:12 +0100 Subject: [PATCH 013/201] Add another one clear of search string for font name --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index acf29d653..f1bffa8c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2037,8 +2037,14 @@ void GLGizmoEmboss::draw_font_list() // change color of hint to normal text bool is_popup_open = ImGui::IsPopupOpen(popup_id); - if (!is_popup_open) + if (!is_popup_open) { ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + + // Fix clearance of search input, + // Sometime happens that search text not disapear after font select + m_face_names.search.clear(); + } + if (ImGui::InputTextWithHint(input_id, selected, &m_face_names.search, input_flags)) { // update filtration result m_face_names.hide = std::vector(m_face_names.faces.size(), {false}); @@ -2052,7 +2058,7 @@ void GLGizmoEmboss::draw_font_list() m_face_names.hide[index] = !start_with; } } - if (!is_popup_open) + if (!is_popup_open) ImGui::PopStyleColor(); // revert changes for hint color const bool is_input_text_active = ImGui::IsItemActive(); @@ -2118,7 +2124,6 @@ void GLGizmoEmboss::draw_font_list() // Just one after close combo box // free texture and set id to zero m_face_names.is_init = false; - m_face_names.search.clear(); m_face_names.hide.clear(); // cancel all process for generation of texture for (FaceName &face : m_face_names.faces) @@ -2129,6 +2134,7 @@ void GLGizmoEmboss::draw_font_list() // Remove value from search input ImGuiWrapper::left_inputs(); + m_face_names.search.clear(); } // delete unloadable face name when try to use From d6eea1de429d59b77553d0e5a30ec5e9ad708ad4 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 6 Feb 2023 10:26:00 +0100 Subject: [PATCH 014/201] Relative move over surface - Not Work --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 123 ++++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 13 +++ 2 files changed, 116 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index f1bffa8c3..b88705ad3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -465,7 +465,9 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { - auto do_move = [&]() { + // Fix when leave window during dragging + // Fix when click right button + if (m_surface_drag.has_value() && !mouse_event.Dragging()) { // write transformation from UI into model m_parent.do_move(L("Surface move")); @@ -479,15 +481,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // allow moving with object again m_parent.enable_moving(true); m_surface_drag.reset(); - }; - if (mouse_event.Moving()) { - // Fix when leave window during dragging and move cursor back - if (m_surface_drag.has_value()) - do_move(); - return false; + // only left up is correct + // otherwise it is fix state and return false + return mouse_event.LeftUp(); } + if (mouse_event.Moving()) + return false; + // detect start text dragging if (mouse_event.LeftDown()) { // exist selected volume? @@ -536,7 +538,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); - m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, gl_volume, condition}; + Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); + m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, volume_tr, gl_volume, condition}; // Cancel job to prevent interuption of dragging (duplicit result) if (m_job_cancel != nullptr) @@ -568,19 +571,85 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); - TextConfiguration &tc = *m_volume->text_configuration; - const FontProp& font_prop = tc.style.prop; - apply_transformation(font_prop, trmat); + // !!!!!!!!!!!!!!!! USE DIRECT hit position and normal - // fix baked transformation from .3mf store process - if (tc.fix_3mf_tr.has_value()) - trmat = trmat * (*tc.fix_3mf_tr); + //TextConfiguration &tc = *m_volume->text_configuration; + //const FontProp& font_prop = tc.style.prop; + //apply_transformation(font_prop, trmat); + + //// fix baked transformation from .3mf store process + //if (tc.fix_3mf_tr.has_value()) + // trmat = trmat * (*tc.fix_3mf_tr); // volume transfomration in world coor - Transform3d world = object_trmat * trmat; - Transform3d volume_tr = m_surface_drag->instance_inv * world; + Transform3d wanted_world = object_trmat * trmat; + Transform3d wanted_volume = m_surface_drag->instance_inv * wanted_world; - // Update transformation inside of instances + // Calculate offset inside instance: + // transformation from curret to wanted position + Vec3d from_position = m_surface_drag->volume_tr * Vec3d::Zero(); + Vec3d to_position = wanted_volume * Vec3d::Zero(); + + Transform3d hit_to_instance = object_trmat * m_surface_drag->instance_inv; + Vec3d to_position2 = hit_to_instance * hit->position.cast(); + Vec3d z_t2 = hit_to_instance.linear() * hit->normal.cast(); + + Vec3d offset_instance = to_position - from_position; + Transform3d trnsl{Eigen::Translation(offset_instance)}; + + // current transformation from volume to world + Transform3d current_world = m_surface_drag->instance_inv.inverse() * m_surface_drag->volume_tr; + + auto current_world_linear = current_world.linear(); + auto wanted_world_linear = wanted_world.linear(); + + // Calculate rotation of Z-vectors from current to wanted position + Transform3d rot = Transform3d::Identity(); + // Transformed unit vector Z direction (f)rom, (t)o + Vec3d z_f = m_surface_drag->volume_tr.linear() * Vec3d::UnitZ(); + Vec3d z_t = wanted_volume.linear() * Vec3d::UnitZ(); + z_f.normalize(); + z_t.normalize(); + double cos_angle = z_t.dot(z_f); + + // Calculate only when angle is not zero + if (cos_angle < 1. && cos_angle > -1.) { + m_surface_drag->from = from_position; + m_surface_drag->to = to_position; + m_surface_drag->from_dir = z_f; + m_surface_drag->to_dir = z_t; + + m_surface_drag->f_tr = current_world; + m_surface_drag->t_tr = wanted_world; + + // TODO: solve opposit direction of z_t and z_f (a.k.a. angle 180 DEG) + // if (cos_angle == 0.) {} + + // Calculate rotation axe from current to wanted inside instance + Vec3d axe = z_t.cross(z_f); + axe.normalize(); + double angle = acos(cos_angle); + rot = Eigen::AngleAxis(-angle, axe); + } + + // Calculate scale in world + auto calc_scale = [¤t_world_linear, wanted_world_linear](const Vec3d &dir) -> double { + Vec3d current = current_world_linear * dir; + Vec3d wanted = wanted_world_linear * dir; + double current_sq = current.squaredNorm(); + double wanted_sq = wanted.squaredNorm(); + return sqrt(wanted_sq / current_sq); + }; + double y_scale = calc_scale(Vec3d::UnitY()); + double z_scale = calc_scale(Vec3d::UnitZ()); + Transform3d scale(Eigen::Scaling(1., y_scale, z_scale)); + + Transform3d volume_tr = trnsl * m_surface_drag->volume_tr * rot; + + assert(volume_tr.matrix()(0,0) == volume_tr.matrix()(0,0)); // Check valid transformation not a NAN + Transform3d volume_tr2 = m_surface_drag->instance_inv * wanted_world; + + // Update transformation inside of instances for (GLVolume *vol : m_parent.get_volumes().volumes) { if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) @@ -593,9 +662,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.set_as_dirty(); return true; - } else if (mouse_event.LeftUp()) { - do_move(); - return true; } return false; } @@ -631,6 +697,23 @@ bool GLGizmoEmboss::on_init() std::string GLGizmoEmboss::on_get_name() const { return _u8L("Emboss"); } void GLGizmoEmboss::on_render() { + // Render debug view to surface move + if (m_surface_drag.has_value()) { + auto glvol = priv::get_gl_volume(m_parent.get_selection()); + auto tr = glvol->get_instance_transformation().get_matrix(); + CoordAxes from; + from.set_origin(m_surface_drag->from); + //from.render(tr, 2.); + + CoordAxes to; + to.set_origin(m_surface_drag->to); + //to.render(tr, 2.); + + CoordAxes axe; + axe.render(m_surface_drag->f_tr); + axe.render(m_surface_drag->t_tr); + } + // no volume selected if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 0cfe34508..610d10e72 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -316,11 +316,24 @@ private: // Help convert world transformation to instance space Transform3d instance_inv; + // Start dragging volume transformation + Transform3d volume_tr; + // Dragged gl volume GLVolume *gl_volume; // condition for raycaster RaycastManager::AllowVolumes condition; + + // Visuzalization + Vec3d from = Vec3d::Zero(); + Vec3d to = Vec3d::Zero(); + + Vec3d from_dir = Vec3d::UnitZ(); + Vec3d to_dir = Vec3d::UnitZ(); + + Transform3d f_tr = Transform3d::Identity(); + Transform3d t_tr = Transform3d::Identity(); }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; From c1b480e57f8aafbcb7a68d6e71064c01265b94d0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 7 Feb 2023 12:10:40 +0100 Subject: [PATCH 015/201] On rayCast miss add part simillar way as right panel do. Fix: https://github.com/prusa3d/PrusaSlicer/issues/9611 --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 28 ++++++++++++++++--------- src/slic3r/Utils/RaycastManager.hpp | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b88705ad3..b7081b210 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -229,13 +229,17 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); - // Try to cast ray into scene and find object for add volume - if (priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager)) - // object found - return; - - // object is not under mouse position soo create object on plater - priv::start_create_object_job(emboss_data, mouse_pos); + if (gl_volume != nullptr) { + // Try to cast ray into scene and find object for add volume + if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager)) { + // When model is broken. It could appear that hit miss the object. + // So add part near by in simmilar manner as right panel do + create_volume(volume_type); + } + } else { + // object is not under mouse position soo create object on plater + priv::start_create_object_job(emboss_data, mouse_pos); + } } // Designed for create volume without information of mouse in scene @@ -266,8 +270,9 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) const GLVolume *vol = nullptr; const Camera &camera = wxGetApp().plater()->get_camera(); priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); - if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) { - assert(vol != nullptr); + if (vol == nullptr) { + priv::start_create_object_job(emboss_data, screen_center); + } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) { // in centroid of convex hull is not hit with object // soo create transfomation on border of object @@ -3924,7 +3929,9 @@ GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { bool priv::start_create_volume_on_surface_job( DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster) { + assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; + Plater *plater = wxGetApp().plater(); const ModelObjectPtrs &objects = plater->model().objects; @@ -3941,7 +3948,8 @@ bool priv::start_create_volume_on_surface_job( // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set // also hit must exist. But there is options to add text by object list - if (!hit.has_value()) return false; + if (!hit.has_value()) + return false; Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key); Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 94da384d2..5451c4e92 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -124,7 +124,7 @@ public: std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; /// - /// Getter on transformation + /// Getter on transformation from hitted volume to world /// /// Define transformation /// Transformation for key From fb488e745b66e1805f937a9b8aff9faccfd7dc4f Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 7 Feb 2023 14:58:04 +0100 Subject: [PATCH 016/201] Allowe cast on surface of corrupted meshes. Skip filtration --> allowe move over surface under the bed --- src/slic3r/Utils/RaycastManager.cpp | 38 ++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 12fc3dfed..be11c7f89 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -121,6 +121,33 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) m_transformations.erase(m_transformations.begin() + i); } +#include "slic3r/GUI/CameraUtils.hpp" +namespace priv { + +// Copy functionality from MeshRaycaster::unproject_on_mesh without filtering +static std::optional unproject_on_mesh(const MeshRaycaster &raycaster, + const Vec2d &mouse_pos, const Transform3d &transformation, const Camera &camera) { + + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv*point; + direction = inv.linear()*direction; + + const AABBMesh &aabb_mesh = raycaster.get_aabb_mesh(); + std::vector hits = aabb_mesh.query_ray_hits(point, direction); + + if (hits.empty()) + return {}; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + return RaycastManager::SurfacePoint( + hit.position().cast(), + hit.normal().cast() + ); +} +} // namespace priv std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const @@ -137,12 +164,11 @@ std::optional RaycastManager::unproject( -> bool { return volume_id == it.first; }); if (raycaster_it == m_raycasters.end()) continue; const MeshRaycaster &raycaster = *(raycaster_it->second); - SurfacePoint surface_point; - bool success = raycaster.unproject_on_mesh( - mouse_pos, transformation, camera, - surface_point.position, surface_point.normal); - if (!success) continue; - + std::optional surface_point_opt = priv::unproject_on_mesh( + raycaster, mouse_pos, transformation, camera); + if (!surface_point_opt.has_value()) + continue; + const SurfacePoint &surface_point = *surface_point_opt; Vec3d act_hit_tr = transformation * surface_point.position.cast(); double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); if (closest.has_value() && From 040f721873aefaa2707f8f61f6fbf83e625dd97d Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 7 Feb 2023 15:23:08 +0100 Subject: [PATCH 017/201] Fix translation mentioned by issue: https://github.com/prusa3d/PrusaSlicer/issues/9583 --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b7081b210..abdeec66e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1703,9 +1703,9 @@ void GLGizmoEmboss::draw_text_input() auto &ff = m_style_manager.get_font_file_with_cache(); float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale); if (imgui_size > StyleManager::max_imgui_font_size) - append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input.")); + append_warning(_u8L("Too tall"), _u8L("Diminished font height inside text input.")); if (imgui_size < StyleManager::min_imgui_font_size) - append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input.")); + append_warning(_u8L("Too small"), _u8L("Enlarged font height inside text input.")); if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who); } From fc9bae667da0a32c6b0f6f72452eeeffd1bd300f Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 9 Feb 2023 09:12:09 +0100 Subject: [PATCH 018/201] separate calculation of rotation --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 162 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 4 +- 2 files changed, 97 insertions(+), 69 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index abdeec66e..84ae36508 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -467,6 +467,41 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu } return nearest_offset; } +namespace priv { + +static bool allign_z(const Vec3d &z_t, Transform3d &rotate) +{ + // Transformed unit vector Z direction (f)rom, (t)o + const Vec3d& z_f = Vec3d::UnitZ(); + Vec3d z_t_norm = z_t.normalized(); + double cos_angle = z_t_norm.dot(z_f); + + // Calculate rotation of Z-vectors from current to wanted position + rotate = Transform3d::Identity(); + + if (cos_angle == 0.) { + // check that direction is not same + if (z_t_norm.z() > 0.) + return false; + + // opposit direction of z_t and z_f (a.k.a. angle 180 DEG) + rotate = Eigen::AngleAxis(M_PI, Vec3d::UnitX()); + return true; + } else if (cos_angle >= 1. || cos_angle <= -1.) { + // bad cas angle value almost zero angle so no rotation + return false; + } + + // Calculate only when angle is not zero + // Calculate rotation axe from current to wanted inside instance + Vec3d axe = z_t_norm.cross(z_f); + axe.normalize(); + double angle = acos(cos_angle); + rotate = Eigen::AngleAxis(-angle, axe); + return true; +} + +} bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { @@ -544,6 +579,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); + TextConfiguration &tc = *m_volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); + m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, volume_tr, gl_volume, condition}; // Cancel job to prevent interuption of dragging (duplicit result) @@ -566,76 +606,38 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw m_parent.set_as_dirty(); return true; } - // Calculate temporary position + // Calculate offset: transformation to wanted position Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); - - // !!!!!!!!!!!!!!!! USE DIRECT hit position and normal - - //TextConfiguration &tc = *m_volume->text_configuration; - //const FontProp& font_prop = tc.style.prop; - //apply_transformation(font_prop, trmat); - - //// fix baked transformation from .3mf store process - //if (tc.fix_3mf_tr.has_value()) - // trmat = trmat * (*tc.fix_3mf_tr); - - // volume transfomration in world coor - Transform3d wanted_world = object_trmat * trmat; - Transform3d wanted_volume = m_surface_drag->instance_inv * wanted_world; - - // Calculate offset inside instance: - // transformation from curret to wanted position - Vec3d from_position = m_surface_drag->volume_tr * Vec3d::Zero(); - Vec3d to_position = wanted_volume * Vec3d::Zero(); - Transform3d hit_to_instance = object_trmat * m_surface_drag->instance_inv; - Vec3d to_position2 = hit_to_instance * hit->position.cast(); - Vec3d z_t2 = hit_to_instance.linear() * hit->normal.cast(); + Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); + Vec3d offset_volume = hit_to_volume * hit->position.cast(); + Transform3d translate{Eigen::Translation(offset_volume)}; - Vec3d offset_instance = to_position - from_position; - Transform3d trnsl{Eigen::Translation(offset_instance)}; + Transform3d rotate; + // normal transformed to volume + Vec3d z_t = hit_to_volume.linear() * hit->normal.cast(); + bool exist_rotate = priv::allign_z(z_t, rotate); + Transform3d volume_tr = m_surface_drag->volume_tr * translate * rotate; + assert(volume_tr.matrix()(0, 0) == volume_tr.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_tr.matrix()(0, 0) != volume_tr.matrix()(0, 0)) + return true; + + // Check scale in world + // current transformation from volume to world Transform3d current_world = m_surface_drag->instance_inv.inverse() * m_surface_drag->volume_tr; - auto current_world_linear = current_world.linear(); - auto wanted_world_linear = wanted_world.linear(); - // Calculate rotation of Z-vectors from current to wanted position - Transform3d rot = Transform3d::Identity(); - // Transformed unit vector Z direction (f)rom, (t)o - Vec3d z_f = m_surface_drag->volume_tr.linear() * Vec3d::UnitZ(); - Vec3d z_t = wanted_volume.linear() * Vec3d::UnitZ(); - z_f.normalize(); - z_t.normalize(); - double cos_angle = z_t.dot(z_f); - - // Calculate only when angle is not zero - if (cos_angle < 1. && cos_angle > -1.) { - m_surface_drag->from = from_position; - m_surface_drag->to = to_position; - m_surface_drag->from_dir = z_f; - m_surface_drag->to_dir = z_t; - - m_surface_drag->f_tr = current_world; - m_surface_drag->t_tr = wanted_world; - - // TODO: solve opposit direction of z_t and z_f (a.k.a. angle 180 DEG) - // if (cos_angle == 0.) {} - - // Calculate rotation axe from current to wanted inside instance - Vec3d axe = z_t.cross(z_f); - axe.normalize(); - double angle = acos(cos_angle); - rot = Eigen::AngleAxis(-angle, axe); - } + Transform3d wanted_world = m_surface_drag->instance_inv.inverse() * volume_tr; + auto wanted_world_linear = wanted_world.linear(); // Calculate scale in world auto calc_scale = [¤t_world_linear, wanted_world_linear](const Vec3d &dir) -> double { @@ -643,18 +645,32 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec3d wanted = wanted_world_linear * dir; double current_sq = current.squaredNorm(); double wanted_sq = wanted.squaredNorm(); - return sqrt(wanted_sq / current_sq); + return sqrt(current_sq / wanted_sq); }; double y_scale = calc_scale(Vec3d::UnitY()); double z_scale = calc_scale(Vec3d::UnitZ()); Transform3d scale(Eigen::Scaling(1., y_scale, z_scale)); + volume_tr = volume_tr * scale; - Transform3d volume_tr = trnsl * m_surface_drag->volume_tr * rot; + // recalculate rotation for scaled volume + //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); + //z_t = hit_to_volume2.linear() * hit->normal.cast(); + //bool exist_rotate2 = priv::allign_z(z_t, rotate); + //volume_tr = m_surface_drag->volume_tr * translate * rotate * scale; - assert(volume_tr.matrix()(0,0) == volume_tr.matrix()(0,0)); // Check valid transformation not a NAN - Transform3d volume_tr2 = m_surface_drag->instance_inv * wanted_world; + const TextConfiguration &tc = *m_volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_tr = volume_tr * (*tc.fix_3mf_tr); - // Update transformation inside of instances + // apply move in Z direction for move with flat surface above texture + const FontProp &prop = tc.style.prop; + if (!prop.use_surface && prop.distance.has_value()) { + Vec3d translate = Vec3d::UnitZ() * (*prop.distance); + volume_tr.translate(translate); + } + + // Update transformation forf all instances for (GLVolume *vol : m_parent.get_volumes().volumes) { if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) @@ -662,7 +678,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) vol->set_volume_transformation(volume_tr); } - // calculate scale + // update scale of selected volume --> should be approx the same calculate_scale(); m_parent.set_as_dirty(); @@ -862,7 +878,13 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) ImVec2 center( mouse_pos.x + m_surface_drag->mouse_offset.x(), mouse_pos.y + m_surface_drag->mouse_offset.y()); - priv::draw_cross_hair(center); + ImU32 color = ImGui::GetColorU32( + m_surface_drag->exist_hit ? + ImVec4(1.f, 1.f, 1.f, .75f) : // transparent white + ImVec4(1.f, .3f, .3f, .75f) + ); // Warning color + const float radius = 16.f; + priv::draw_cross_hair(center, radius, color); } #ifdef SHOW_FINE_POSITION @@ -1115,18 +1137,22 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() EmbossStyles GLGizmoEmboss::create_default_styles() { wxFont wx_font_normal = *wxNORMAL_FONT; - wxFont wx_font_small = *wxSMALL_FONT; - #ifdef __APPLE__ - wx_font_normal.SetFaceName("Helvetica"); - wx_font_small.SetFaceName("Helvetica"); + // Set normal font to helvetica when possible + wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding); + for (const wxString &facename : facenames) { + if (facename.IsSameAs("Helvetica")) { + wx_font_normal = wxFont(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding)); + break; + } + } #endif // __APPLE__ // https://docs.wxwidgets.org/3.0/classwx_font.html // Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT EmbossStyles styles = { WxFontUtils::create_emboss_style(wx_font_normal, _u8L("NORMAL")), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) - WxFontUtils::create_emboss_style(wx_font_normal, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT. + WxFontUtils::create_emboss_style(*wxSMALL_FONT, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT. WxFontUtils::create_emboss_style(*wxITALIC_FONT, _u8L("ITALIC")), // A font using the wxFONTFAMILY_ROMAN family and wxFONTSTYLE_ITALIC style and of the same size of wxNORMAL_FONT. WxFontUtils::create_emboss_style(*wxSWISS_FONT, _u8L("SWISS")), // A font identic to wxNORMAL_FONT except for the family used which is wxFONTFAMILY_SWISS. WxFontUtils::create_emboss_style(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("MODERN")), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 610d10e72..0c9b3b98e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -255,7 +255,7 @@ private: std::vector bad = {}; // Configuration of font encoding - const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; + static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; // Identify if preview texture exists GLuint texture_id = 0; @@ -325,6 +325,8 @@ private: // condition for raycaster RaycastManager::AllowVolumes condition; + bool exist_hit = true; + // Visuzalization Vec3d from = Vec3d::Zero(); Vec3d to = Vec3d::Zero(); From 88af7762fcd766343425672c08e4f90db0c76632 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 13 Feb 2023 12:07:31 +0100 Subject: [PATCH 019/201] Remove dependency on main app in job --- .../GUI/Jobs/CreateFontStyleImagesJob.cpp | 57 ++++++++----------- .../GUI/Jobs/CreateFontStyleImagesJob.hpp | 6 +- src/slic3r/Utils/EmbossStyleManager.cpp | 16 +++++- src/slic3r/Utils/EmbossStyleManager.hpp | 3 + 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp index d1a671330..8aa9e23cb 100644 --- a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp @@ -2,11 +2,6 @@ // rasterization of ExPoly #include "libslic3r/SLA/AGGRaster.hpp" - -// for get DPI -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/MainFrame.hpp" - #include "slic3r/GUI/3DScene.hpp" // ::glsafe // ability to request new frame after finish rendering @@ -20,15 +15,15 @@ using namespace Slic3r::GUI; using namespace Slic3r::GUI::Emboss; -CreateFontStyleImagesJob::CreateFontStyleImagesJob( - StyleManager::StyleImagesData &&input) - : m_input(std::move(input)) +CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input) + : m_input(std::move(input)), m_width(0), m_height(0) { assert(m_input.result != nullptr); assert(!m_input.styles.empty()); assert(!m_input.text.empty()); assert(m_input.max_size.x() > 1); assert(m_input.max_size.y() > 1); + assert(m_input.ppm > 1e-5); } void CreateFontStyleImagesJob::process(Ctl &ctl) @@ -36,7 +31,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) // create shapes and calc size (bounding boxes) std::vector name_shapes(m_input.styles.size()); std::vector scales(m_input.styles.size()); - images = std::vector(m_input.styles.size()); + m_images = std::vector(m_input.styles.size()); for (auto &item : m_input.styles) { size_t index = &item - &m_input.styles.front(); @@ -44,21 +39,17 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) shapes = text2shapes(item.font, m_input.text.c_str(), item.prop); // create image description - StyleManager::StyleImage &image = images[index]; + StyleManager::StyleImage &image = m_images[index]; BoundingBox &bounding_box = image.bounding_box; for (ExPolygon &shape : shapes) bounding_box.merge(BoundingBox(shape.contour.points)); for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min); // calculate conversion from FontPoint to screen pixels by size of font - auto mf = wxGetApp().mainframe; - // dot per inch for monitor - int dpi = get_dpi_for_window(mf); - double ppm = dpi / 25.4; // pixel per milimeter const auto &cn = item.prop.collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; double unit_per_em = item.font.font_file->infos[font_index].unit_per_em; - double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm; + double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * m_input.ppm; scales[index] = scale; //double scale = font_prop.size_in_mm * SCALING_FACTOR; @@ -78,30 +69,30 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) // arrange bounding boxes int offset_y = 0; - width = 0; - for (StyleManager::StyleImage &image : images) { + m_width = 0; + for (StyleManager::StyleImage &image : m_images) { image.offset.y() = offset_y; offset_y += image.tex_size.y+1; - if (width < image.tex_size.x) - width = image.tex_size.x; + if (m_width < image.tex_size.x) + m_width = image.tex_size.x; } - height = offset_y; - for (StyleManager::StyleImage &image : images) { + m_height = offset_y; + for (StyleManager::StyleImage &image : m_images) { const Point &o = image.offset; const ImVec2 &s = image.tex_size; - image.uv0 = ImVec2(o.x() / (double) width, - o.y() / (double) height); - image.uv1 = ImVec2((o.x() + s.x) / (double) width, - (o.y() + s.y) / (double) height); + image.uv0 = ImVec2(o.x() / (double) m_width, + o.y() / (double) m_height); + image.uv1 = ImVec2((o.x() + s.x) / (double) m_width, + (o.y() + s.y) / (double) m_height); } // Set up result - pixels = std::vector(4*width * height, {255}); + m_pixels = std::vector(4 * m_width * m_height, {255}); // upload sub textures - for (StyleManager::StyleImage &image : images) { + for (StyleManager::StyleImage &image : m_images) { sla::Resolution resolution(image.tex_size.x, image.tex_size.y); - size_t index = &image - &images.front(); + size_t index = &image - &m_images.front(); double pixel_dim = SCALING_FACTOR / scales[index]; sla::PixelDim dim(pixel_dim, pixel_dim); double gamma = 1.; @@ -110,7 +101,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) for (const ExPolygon &shape : name_shapes[index]) r->draw(shape); // copy rastered data to pixels - sla::RasterEncoder encoder = [&offset = image.offset, &pix = pixels, w=width,h=height] + sla::RasterEncoder encoder = [&offset = image.offset, &pix = m_pixels, w=m_width,h=m_height] (const void *ptr, size_t width, size_t height, size_t num_components) { // bigger value create darker image unsigned char gray_level = 5; @@ -142,18 +133,18 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &) glsafe(::glBindTexture(target, tex_id)); glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - GLint w = width, h=height; + GLint w = m_width, h = m_height; glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type, - (const void *) pixels.data())); + (const void *) m_pixels.data())); // set up texture id void *texture_id = (void *) (intptr_t) tex_id; - for (StyleManager::StyleImage &image : images) + for (StyleManager::StyleImage &image : m_images) image.texture_id = texture_id; // move to result m_input.result->styles = std::move(m_input.styles); - m_input.result->images = std::move(images); + m_input.result->images = std::move(m_images); // bind default texture GLuint no_texture_id = 0; diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp index c220f2ee0..b8c2757a6 100644 --- a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp @@ -19,11 +19,11 @@ class CreateFontStyleImagesJob : public Job // Output data // texture size - int width, height; + int m_width, m_height; // texture data - std::vector pixels; + std::vector m_pixels; // descriptors of sub textures - std::vector images; + std::vector m_images; public: CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input); diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index 9c6738ece..af1aae669 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -304,12 +304,15 @@ void StyleManager::init_trunc_names(float max_width) { } } -#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp" - // for access to worker #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" +// for get DPI +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/MainFrame.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" + void StyleManager::init_style_images(const Vec2i &max_size, const std::string &text) { @@ -361,8 +364,15 @@ void StyleManager::init_style_images(const Vec2i &max_size, style.prop }); } + + auto mf = wxGetApp().mainframe; + // dot per inch for monitor + int dpi = get_dpi_for_window(mf); + // pixel per milimeter + double ppm = dpi / ObjectManipulation::in_to_mm; + auto &worker = wxGetApp().plater()->get_ui_job_worker(); - StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images}; + StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images, ppm}; queue_job(worker, std::make_unique(std::move(data))); } diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 8183214e9..08fa72642 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -277,6 +277,9 @@ private: // place to store result in main thread in Finalize std::shared_ptr result; + + // pixel per milimeter (scaled DPI) + double ppm; }; std::shared_ptr m_temp_style_images; bool m_exist_style_images; From f42ae64277069c5fd3cd641cd845a4d59aae6097 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 14 Feb 2023 16:06:59 +0100 Subject: [PATCH 020/201] Change Text property after move over surface when world size is changed(re-create text volume). NOTE: Not intuitive during dragging. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 348 +++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 8 + 2 files changed, 290 insertions(+), 66 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 84ae36508..e602bbd76 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -467,21 +467,20 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu } return nearest_offset; } -namespace priv { -static bool allign_z(const Vec3d &z_t, Transform3d &rotate) +namespace priv { +static bool allign_vec(const Vec3d &z_f, const Vec3d &z_t, Transform3d &rotate) { - // Transformed unit vector Z direction (f)rom, (t)o - const Vec3d& z_f = Vec3d::UnitZ(); - Vec3d z_t_norm = z_t.normalized(); - double cos_angle = z_t_norm.dot(z_f); + Vec3d z_f_norm = z_f.normalized(); + Vec3d z_t_norm = z_t.normalized(); + double cos_angle = z_t_norm.dot(z_f_norm); // Calculate rotation of Z-vectors from current to wanted position rotate = Transform3d::Identity(); if (cos_angle == 0.) { // check that direction is not same - if (z_t_norm.z() > 0.) + if (z_t_norm.z() > 0.) return false; // opposit direction of z_t and z_f (a.k.a. angle 180 DEG) @@ -494,13 +493,187 @@ static bool allign_z(const Vec3d &z_t, Transform3d &rotate) // Calculate only when angle is not zero // Calculate rotation axe from current to wanted inside instance - Vec3d axe = z_t_norm.cross(z_f); + Vec3d axe = z_t_norm.cross(z_f_norm); axe.normalize(); double angle = acos(cos_angle); - rotate = Eigen::AngleAxis(-angle, axe); + rotate = Eigen::AngleAxis(-angle, axe); return true; } +static bool allign_z(const Vec3d &z_t, Transform3d &rotate) +{ + // Transformed unit vector Z direction (f)rom, (t)o + const Vec3d& z_f = Vec3d::UnitZ(); + return allign_vec(Vec3d::UnitZ(), z_t, rotate); +} + + // Calculate scale in world +static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) +{ + Vec3d from_dir = from * dir; + Vec3d to_dir = to * dir; + double from_scale_sq = from_dir.squaredNorm(); + double to_scale_sq = to_dir.squaredNorm(); + if (is_approx(from_scale_sq, to_scale_sq, 1e-3)) + return {}; // no scale + return sqrt(from_scale_sq / to_scale_sq); +}; + +// Copy from branch et_transformation --> Geometry +// suggested by @bubnikv +void reset_skew(Transform3d& m) +{ + auto new_scale_factor = [](const Matrix3d& s) { + return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average + }; + + const Eigen::JacobiSVD svd(m.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); + Matrix3d u = svd.matrixU(); + Matrix3d v = svd.matrixV(); + Matrix3d s = svd.singularValues().asDiagonal(); + + //Matrix3d mirror; + m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; +} + +// Multiply from right +static Transform3d surface_transformR(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { + Transform3d to_volume = (i * v).inverse(); + Vec3d offset_volume = to_volume * p; + Transform3d translate{Eigen::Translation(offset_volume)}; + + Transform3d rotate; + // normal transformed to volume + Vec3d z_t = to_volume.linear() * n; + bool exist_rotate = priv::allign_z(z_t, rotate); + return v * translate * rotate; +} + +/// +/// Create transformation for volume to move over surface +/// Multiply from Left side - NOT WORK - with scaled instances +/// +/// Point in world coordinate +/// Normal in world coordinate - orientation +/// Original volume transformation +/// Instance transformation +/// Transformation of volume to lay on surface +static Transform3d surface_transformL(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { + // w .. original world + Transform3d w = i * v; + + // remove already existing of skew before calc rotation + // priv::reset_skew(w); + + // z .. unit z vector in world coordinate + Vec3d z = w.linear() * Vec3d::UnitZ(); + Transform3d rot = Transform3d::Identity(); + bool exist_rot = priv::allign_vec(z, n, rot); + + // rot_w .. new rotation applied on world + Transform3d rot_w = rot * w; + + // p0 .. Zero of volume in world + Vec3d p0 = rot_w * Vec3d::Zero(); + Vec3d offset = p - p0; // in world + Transform3d tr{Eigen::Translation(offset)}; + + // w2 .. wanted world transformation + Transform3d w2 = tr * rot_w; + + //priv::reset_skew(w2); + + // _ .. inverse + // i_ .. instance inverse + Transform3d i_ = i.inverse(); + + // w = i * v \\ left multiply by i_ + // i_ * w = i_ * i * v + // v = i_ * w + return i_ * w2; + // NOTE: Do not keep scale of text when move over scaled instance +} + +// transformation inside of instance +static Transform3d surface_transform2(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) +{ + // _ .. inverse + // i_ .. instance inverse + Transform3d i_ = i.inverse(); + Vec3d pp = i_ * p; + Vec3d nn = i_.linear() * n.normalized(); + nn.normalize(); + + // z .. unit z vector in world coordinate + Vec3d z = v * Vec3d::UnitZ(); + z.normalize(); + + Transform3d rot = Transform3d::Identity(); + bool exist_rot = priv::allign_vec(z, nn, rot); + + // rot_w .. new rotation applied on world + Transform3d rotated = rot * v; + + // p0 .. Zero of volume in world + Vec3d p0 = rotated * Vec3d::Zero(); + Vec3d offset = pp - p0; // in world + Transform3d tr{Eigen::Translation(offset)}; + Transform3d volume_new = tr * rotated; + //return volume_new; + + // Remove skew in world + Transform3d world_new = i * volume_new; + reset_skew(world_new); + volume_new = i_ * world_new; + + return volume_new; +} + +// work in space defined by SVD +static Transform3d surface_transform3(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { + // w .. original world + Transform3d w = i * v; + + const Eigen::JacobiSVD svd1(w.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); + Matrix3d u1 = svd1.matrixU(); + Matrix3d v1 = svd1.matrixV(); + Matrix3d s1 = svd1.singularValues().asDiagonal(); + Transform3d tr1(Eigen::Translation3d(w.translation())); + //Transform3d test = tr1 * Transform3d(u1 * s1 * v1.transpose()); + + // modification of world + Transform3d mod(s1 * v1.transpose()); + Transform3d mod_ = mod.inverse(); + Transform3d w_mod = w * mod_; + + Vec3d nn = mod_.linear() * n; + // z .. unit z vector in world coordinate + Vec3d z = w_mod.linear() * Vec3d::UnitZ(); + Transform3d rot = Transform3d::Identity(); + bool exist_rot = priv::allign_vec(z, nn, rot); + + // rot_w .. new rotation applied on world + Transform3d rot_w = rot * w; + + // p0 .. Zero of volume in world + Vec3d p0 = rot_w * Vec3d::Zero(); + Vec3d offset = p - p0; // in world + Transform3d tr{Eigen::Translation(offset)}; + + // w2 .. wanted world transformation + Transform3d w2 = tr * rot_w; + + // _ .. inverse + // i_ .. instance inverse + Transform3d i_ = i.inverse(); + + // w = i * v \\ left multiply by i_ + // i_ * w = i_ * i * v + // v = i_ * w + return i_ * w2; + // NOTE: Do not keep scale of text when move over scaled instance +} + } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -512,7 +685,19 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.do_move(L("Surface move")); // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) + bool need_process = m_volume->text_configuration->style.prop.use_surface; + + if (m_surface_drag->y_scale.has_value()) { + m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); + need_process |= set_height(); + } + + if (m_surface_drag->z_scale.has_value()) { + m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); + need_process |= set_depth(); + } + + if (need_process) process(); // calculate scale @@ -612,45 +797,52 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.set_as_dirty(); return true; } - // Calculate offset: transformation to wanted position - Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d hit_to_instance = object_trmat * m_surface_drag->instance_inv; + Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); + //priv::reset_skew(hit_to_world); + + Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); - Vec3d offset_volume = hit_to_volume * hit->position.cast(); + + Vec3d hit_position = hit->position.cast(); + Vec3d offset_volume = hit_to_volume * hit_position; Transform3d translate{Eigen::Translation(offset_volume)}; Transform3d rotate; // normal transformed to volume - Vec3d z_t = hit_to_volume.linear() * hit->normal.cast(); + Vec3d hit_normal = hit->normal.cast(); + Vec3d z_t = hit_to_volume.linear() * hit_normal; bool exist_rotate = priv::allign_z(z_t, rotate); + // Edit position from right + Transform3d volume_new = m_surface_drag->volume_tr * translate * rotate; - Transform3d volume_tr = m_surface_drag->volume_tr * translate * rotate; - assert(volume_tr.matrix()(0, 0) == volume_tr.matrix()(0, 0)); // Check valid transformation not a NAN - if (volume_tr.matrix()(0, 0) != volume_tr.matrix()(0, 0)) + const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); + const Transform3d &volume = m_surface_drag->volume_tr; + + Vec3d hit_position_world = hit_to_world * hit_position; + Vec3d hit_normal_world = hit_to_world.linear() * hit_normal; + + // REWRITE transformation + Transform3d volume_R = priv::surface_transformR(hit_position_world, hit_normal_world, volume, instance); + Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); + Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); + Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); + //volume_new = volume_L; + + assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) return true; // Check scale in world - - // current transformation from volume to world - Transform3d current_world = m_surface_drag->instance_inv.inverse() * m_surface_drag->volume_tr; + // Calculate Scale to keep size after move over scaled surface + Transform3d current_world = instance * volume; auto current_world_linear = current_world.linear(); - Transform3d wanted_world = m_surface_drag->instance_inv.inverse() * volume_tr; + Transform3d wanted_world = instance * volume_new; auto wanted_world_linear = wanted_world.linear(); - // Calculate scale in world - auto calc_scale = [¤t_world_linear, wanted_world_linear](const Vec3d &dir) -> double { - Vec3d current = current_world_linear * dir; - Vec3d wanted = wanted_world_linear * dir; - double current_sq = current.squaredNorm(); - double wanted_sq = wanted.squaredNorm(); - return sqrt(current_sq / wanted_sq); - }; - double y_scale = calc_scale(Vec3d::UnitY()); - double z_scale = calc_scale(Vec3d::UnitZ()); - Transform3d scale(Eigen::Scaling(1., y_scale, z_scale)); - volume_tr = volume_tr * scale; + m_surface_drag->y_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitY()); + m_surface_drag->z_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitZ()); // recalculate rotation for scaled volume //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); @@ -661,13 +853,13 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) const TextConfiguration &tc = *m_volume->text_configuration; // fix baked transformation from .3mf store process if (tc.fix_3mf_tr.has_value()) - volume_tr = volume_tr * (*tc.fix_3mf_tr); + volume_new = volume_new * (*tc.fix_3mf_tr); // apply move in Z direction for move with flat surface above texture const FontProp &prop = tc.style.prop; if (!prop.use_surface && prop.distance.has_value()) { Vec3d translate = Vec3d::UnitZ() * (*prop.distance); - volume_tr.translate(translate); + volume_new.translate(translate); } // Update transformation forf all instances @@ -675,7 +867,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) continue; - vol->set_volume_transformation(volume_tr); + vol->set_volume_transformation(volume_new); } // update scale of selected volume --> should be approx the same @@ -1136,10 +1328,12 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() EmbossStyles GLGizmoEmboss::create_default_styles() { + wxFontEnumerator::InvalidateCache(); + wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding); + wxFont wx_font_normal = *wxNORMAL_FONT; #ifdef __APPLE__ // Set normal font to helvetica when possible - wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding); for (const wxString &facename : facenames) { if (facename.IsSameAs("Helvetica")) { wx_font_normal = wxFont(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding)); @@ -1181,7 +1375,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles() // No valid style in defult list // at least one style must contain loadable font - wxArrayString facenames = wxFontEnumerator::GetFacenames(wxFontEncoding::wxFONTENCODING_SYSTEM); wxFont wx_font; for (const wxString &face : facenames) { wx_font = wxFont(face); @@ -3082,6 +3275,31 @@ void GLGizmoEmboss::draw_style_edit() { #endif // SHOW_WX_WEIGHT_INPUT } +bool GLGizmoEmboss::set_height() { + float &value = m_style_manager.get_style().prop.size_in_mm; + + // size can't be zero or negative + priv::Limits::apply(value, priv::limits.size_in_mm); + + if (m_volume == nullptr || !m_volume->text_configuration.has_value()) { + assert(false); + return false; + } + + // only different value need process + if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) + return false; + + // store font size into path serialization + const std::optional &wx_font_opt = m_style_manager.get_wx_font(); + if (wx_font_opt.has_value()) { + wxFont wx_font = *wx_font_opt; + wx_font.SetPointSize(static_cast(value)); + m_style_manager.set_wx_font(wx_font); + } + return true; +} + void GLGizmoEmboss::draw_height(bool use_inch) { float &value = m_style_manager.get_style().prop.size_in_mm; @@ -3090,24 +3308,21 @@ void GLGizmoEmboss::draw_height(bool use_inch) const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm"); const std::string revert_text_size = _u8L("Revert text size."); const std::string& name = m_gui_cfg->translations.size; - if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) { - // size can't be zero or negative - priv::Limits::apply(value, priv::limits.size_in_mm); - // only different value need process - if (!is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) { - // store font size into path - EmbossStyle &style = m_style_manager.get_style(); - if (style.type == WxFontUtils::get_actual_type()) { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - if (wx_font_opt.has_value()) { - wxFont wx_font = *wx_font_opt; - wx_font.SetPointSize(static_cast(value)); - m_style_manager.set_wx_font(wx_font); - } - } + if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) + if (set_height()) process(); - } - } +} + + +bool GLGizmoEmboss::set_depth() +{ + float &value = m_style_manager.get_style().prop.emboss; + + // size can't be zero or negative + priv::Limits::apply(value, priv::limits.emboss); + + // only different value need process + return !is_approx(value, m_volume->text_configuration->style.prop.emboss); } void GLGizmoEmboss::draw_depth(bool use_inch) @@ -3118,11 +3333,9 @@ void GLGizmoEmboss::draw_depth(bool use_inch) const std::string revert_emboss_depth = _u8L("Revert embossed depth."); const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm"); const std::string name = m_gui_cfg->translations.depth; - if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth)) { - // size can't be zero or negative - priv::Limits::apply(value, priv::limits.emboss); - process(); - } + if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth)) + if (set_depth()) + process(); } @@ -3977,14 +4190,17 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key); - Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); + Transform3d hit_to_world = raycaster.get_transformation(hit->tr_key); + // priv::reset_skew(hit_to_world); + Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); - const FontProp &font_prop = emboss_data.text_configuration.style.prop; + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat; + Transform3d world_new = hit_to_world * surface_trmat; + // priv::reset_skew(world_new); + Transform3d volume_trmat = instance.inverse() * world_new; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 0c9b3b98e..e1fded204 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -112,6 +112,11 @@ private: void draw_height(bool use_inch); void draw_depth(bool use_inch); + // call after set m_style_manager.get_style().prop.size_in_mm + bool set_height(); + // call after set m_style_manager.get_style().prop.emboss + bool set_depth(); + bool draw_italic_button(); bool draw_bold_button(); void draw_advanced(); @@ -336,6 +341,9 @@ private: Transform3d f_tr = Transform3d::Identity(); Transform3d t_tr = Transform3d::Identity(); + + std::optional y_scale; + std::optional z_scale; }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; From 89ff154f9b8f91d0929cc726575927c97f49b2b0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 14 Feb 2023 18:16:05 +0100 Subject: [PATCH 021/201] temp --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e602bbd76..35aec716e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -799,8 +799,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) } // Calculate offset: transformation to wanted position Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); - //priv::reset_skew(hit_to_world); - Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); From 6d0d24eecf0f172a2cdfb272fe6196b08fcc9169 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 15 Feb 2023 11:05:20 +0100 Subject: [PATCH 022/201] Get GL volume by volume by selection (not hovered volume) --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 76 +++++++++++++++++++++---- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 056719c19..44e664f8f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -375,6 +375,7 @@ static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVol /// Containe what is selected /// Slected when only one volume otherwise nullptr static const GLVolume *get_gl_volume(const Selection &selection); +static GLVolume *get_gl_volume(const GLCanvas3D &canvas); /// /// Get transformation to world @@ -395,6 +396,7 @@ static void change_window_position(std::optional &output_window_offset, } // namespace priv const GLVolume *priv::get_gl_volume(const Selection &selection) { + // return selection.get_first_volume(); const auto &list = selection.get_volume_idxs(); if (list.size() != 1) return nullptr; @@ -402,6 +404,19 @@ const GLVolume *priv::get_gl_volume(const Selection &selection) { return selection.get_volume(volume_idx); } +GLVolume *priv::get_gl_volume(const GLCanvas3D &canvas) { + const GLVolume *gl_volume = get_gl_volume(canvas.get_selection()); + if (gl_volume == nullptr) + return nullptr; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + for (GLVolume *v : gl_volumes) + if (v->composite_id == gl_volume->composite_id) + return v; + + return nullptr; +} + Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) { if (!gl_volume) @@ -536,17 +551,40 @@ void reset_skew(Transform3d& m) m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; } +void reset_skew_respect_z(Transform3d &m) +{ + Vec3d z_before = m * Vec3d::UnitZ(); + priv::reset_skew(m); + Vec3d z_after = m * Vec3d::UnitZ(); + + Transform3d rot; // = Transform3d::Identity(); + if (priv::allign_vec(z_after, z_before, rot)) + m = rot * m; +} + // Multiply from right static Transform3d surface_transformR(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { Transform3d to_volume = (i * v).inverse(); Vec3d offset_volume = to_volume * p; Transform3d translate{Eigen::Translation(offset_volume)}; + + // new transformation for volume + Transform3d v_new = v * translate; + // rotation when exists Transform3d rotate; + // normal transformed to volume Vec3d z_t = to_volume.linear() * n; - bool exist_rotate = priv::allign_z(z_t, rotate); - return v * translate * rotate; + if (priv::allign_z(z_t, rotate)) + v_new = v_new * rotate; + + // Reset skew in world + Transform3d w_new = i * v_new; + priv::reset_skew_respect_z(w_new); + v_new = i.inverse() * w_new; + + return v_new; } /// @@ -581,7 +619,7 @@ static Transform3d surface_transformL(const Vec3d &p, const Vec3d &n, const Tran // w2 .. wanted world transformation Transform3d w2 = tr * rot_w; - //priv::reset_skew(w2); + //priv::reset_skew_respect_z(w2); // _ .. inverse // i_ .. instance inverse @@ -721,15 +759,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; - // must exist hover object - int hovered_id = m_parent.get_first_hover_volume_idx(); - if (hovered_id < 0) + if (m_parent.get_first_hover_volume_idx() < 0) return false; - GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id]; - const ModelObjectPtrs &objects = m_parent.get_model()->objects; + GLVolume *gl_volume = priv::get_gl_volume(m_parent); + if (gl_volume == nullptr) + return false; // hovered object must be actual text volume + const ModelObjectPtrs &objects = m_parent.get_model()->objects; if (m_volume != priv::get_model_volume(gl_volume, objects)) return false; @@ -825,7 +863,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); - //volume_new = volume_L; + volume_new = volume_R; assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) @@ -3701,6 +3739,21 @@ void GLGizmoEmboss::draw_advanced() ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str()); } + ImGui::SameLine(); + if (ImGui::Button(_u8L("Reset scale").c_str())) { + GLVolume *gl_volume = priv::get_gl_volume(m_parent); + if (gl_volume != nullptr) { + Transform3d w = gl_volume->world_matrix(); + priv::reset_skew_respect_z(w); + Transform3d i = gl_volume->get_instance_transformation().get_matrix(); + Transform3d v_new = i.inverse() * w; + gl_volume->set_volume_transformation(v_new); + m_parent.do_move(L("Reset scale")); + } + } else if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); + } + #ifdef ALLOW_DEBUG_MODE ImGui::Text("family = %s", (font_prop.family.has_value() ? font_prop.family->c_str() : @@ -4197,7 +4250,10 @@ bool priv::start_create_volume_on_surface_job( const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); Transform3d world_new = hit_to_world * surface_trmat; - // priv::reset_skew(world_new); + + // Reset skew + priv::reset_skew_respect_z(world_new); + Transform3d volume_trmat = instance.inverse() * world_new; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); return true; From 67155e8da01acc7b78391a6144ec94c2e52fe25b Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 16 Feb 2023 13:09:29 +0100 Subject: [PATCH 023/201] RaycastManager use directly AABBMesh instead of MeshRayCaster --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 38 ++-- src/slic3r/Utils/RaycastManager.cpp | 261 +++++++++++++----------- src/slic3r/Utils/RaycastManager.hpp | 82 +++++--- 3 files changed, 215 insertions(+), 166 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 44e664f8f..c6ce9da8f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -559,7 +559,7 @@ void reset_skew_respect_z(Transform3d &m) Transform3d rot; // = Transform3d::Identity(); if (priv::allign_vec(z_after, z_before, rot)) - m = rot * m; + m = m * rot; } // Multiply from right @@ -3489,21 +3489,33 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); // Try to find closest point when no hit object in emboss direction - if (!hit_opt.has_value()) - hit_opt = raycast_manager.closest(point); + if (!hit_opt.has_value()) { + std::optional close_point_opt = raycast_manager.closest(point); - // It should NOT appear. Closest point always exists. - if (!hit_opt.has_value()) - return {}; + // It should NOT appear. Closest point always exists. + assert(close_point_opt.has_value()); + if (!close_point_opt.has_value()) + return {}; + + // It is no neccesary to move with origin by very small value + if (close_point_opt->squared_distance < EPSILON) + return {}; + + const RaycastManager::ClosePoint &close_point = *close_point_opt; + Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); + Vec3d hit_world = hit_tr * close_point.point; + Vec3d offset_world = hit_world - point; // vector in world + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; + } // It is no neccesary to move with origin by very small value - if (hit_opt->squared_distance < EPSILON) - return {}; - const RaycastManager::Hit &hit = *hit_opt; - Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); - Vec3d hit_world = hit_tr * hit.position.cast(); - Vec3d offset_world = hit_world - point; // vector in world + if (hit.squared_distance < EPSILON) + return {}; + Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); + Vec3d hit_world = hit_tr * hit.position; + Vec3d offset_world = hit_world - point; // vector in world // TIP: It should be close to only z move Vec3d offset_volume = to_world.inverse().linear() * offset_world; return offset_volume; @@ -4246,7 +4258,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position.cast(), hit->normal.cast()); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); Transform3d world_new = hit_to_world * surface_trmat; diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index be11c7f89..fad41424f 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -3,69 +3,50 @@ // include for earn camera #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/CameraUtils.hpp" using namespace Slic3r::GUI; namespace priv { - using namespace Slic3r; -// copied from private part of RaycastManager.hpp -using Raycaster = std::pair >; -// ModelVolume.id +static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip); +static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); +static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ + return std::make_pair(instance->id().id, volume->id().id); } +static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { + return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } +static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { + return is_lower_key(i1.first, i2.first); }; -using Raycasters = std::vector; + // Copy functionality from MeshRaycaster::unproject_on_mesh without filtering +using SurfacePoint = RaycastManager::SurfacePoint; +static std::optional unproject_on_mesh(const AABBMesh &aabb_mesh, + const Vec2d &mouse_pos, + const Transform3d &transformation, + const Camera &camera); -static void actualize(Raycasters &casters, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) -{ - // check if volume was removed - std::vector removed_casters(casters.size(), {true}); - // actualize MeshRaycaster - for (const ModelVolume *volume : volumes) { - size_t oid = volume->id().id; - if (skip != nullptr && skip->skip(oid)) - continue; - auto item = std::find_if(casters.begin(), casters.end(), - [oid](const Raycaster &it) -> bool { return oid == it.first; }); - if (item == casters.end()) { - // add new raycaster - auto raycaster = std::make_unique(volume->get_mesh_shared_ptr()); - casters.emplace_back(std::make_pair(oid, std::move(raycaster))); - } else { - size_t index = item - casters.begin(); - removed_casters[index] = false; - } - } - - // clean other raycasters - for (int i = removed_casters.size() - 1; i >= 0; --i) - if (removed_casters[i]) - casters.erase(casters.begin() + i); -} } void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) { // actualize MeshRaycaster - priv::actualize(m_raycasters, object->volumes, skip); + priv::actualize(m_meshes, object->volumes, skip); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); - + + bool need_sort = false; // actualize transformation matrices for (const ModelVolume *volume : object->volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); for (const ModelInstance *instance : object->instances) { - const Transform3d &instrance_tr = instance->get_matrix(); - Transform3d transformation = instrance_tr * volume_tr; - TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); - auto item = std::find_if(m_transformations.begin(), - m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { - return it.first == tr_key; - }); + const Transform3d &instrance_tr = instance->get_matrix(); + Transform3d transformation = instrance_tr * volume_tr; + TrKey key = priv::create_key(volume, instance); + auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -73,8 +54,8 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back( - std::make_pair(tr_key, transformation)); + m_transformations.emplace_back(std::make_pair(key, transformation)); + need_sort = true; } } } @@ -83,17 +64,21 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); + + if (need_sort) + std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { const ModelVolumePtrs &volumes = instance->get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_raycasters, volumes, skip); + priv::actualize(m_meshes, volumes, skip); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); + bool need_sort = false; // actualize transformation matrices for (const ModelVolume *volume : volumes) { if (skip != nullptr && skip->skip(volume->id().id)) @@ -101,9 +86,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) const Transform3d &volume_tr = volume->get_matrix(); const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); - auto item = std::find_if(m_transformations.begin(), m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { return it.first == tr_key; }); + TrKey key = priv::create_key(volume, instance); + auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -111,7 +95,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(tr_key, transformation)); + m_transformations.emplace_back(std::make_pair(key, transformation)); + need_sort = true; } } @@ -119,65 +104,33 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); + + if (need_sort) + std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -#include "slic3r/GUI/CameraUtils.hpp" -namespace priv { - -// Copy functionality from MeshRaycaster::unproject_on_mesh without filtering -static std::optional unproject_on_mesh(const MeshRaycaster &raycaster, - const Vec2d &mouse_pos, const Transform3d &transformation, const Camera &camera) { - - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - Transform3d inv = transformation.inverse(); - point = inv*point; - direction = inv.linear()*direction; - - const AABBMesh &aabb_mesh = raycaster.get_aabb_mesh(); - std::vector hits = aabb_mesh.query_ray_hits(point, direction); - - if (hits.empty()) - return {}; // no intersection found - - const AABBMesh::hit_result &hit = hits.front(); - return RaycastManager::SurfacePoint( - hit.position().cast(), - hit.normal().cast() - ); -} -} // namespace priv - std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; - size_t volume_id = key.second; + size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - auto raycaster_it = - std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) - -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); - std::optional surface_point_opt = priv::unproject_on_mesh( - raycaster, mouse_pos, transformation, camera); + auto surface_point_opt = + priv::unproject_on_mesh(*mesh, mouse_pos, transformation, camera); if (!surface_point_opt.has_value()) continue; - const SurfacePoint &surface_point = *surface_point_opt; - Vec3d act_hit_tr = transformation * surface_point.position.cast(); + Vec3d act_hit_tr = transformation * surface_point_opt->position.cast(); double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - closest = Hit(key, surface_point, squared_distance); + closest = Hit{*surface_point_opt, key, squared_distance}; } - - //if (!closest.has_value()) return {}; return closest; } @@ -186,16 +139,11 @@ std::optional RaycastManager::unproject(const Vec3d &point, std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; - size_t volume_id = key.second; + size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; const Transform3d &transformation = item.second; - auto raycaster_it = - std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) - -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); - const AABBMesh& mesh = raycaster.get_aabb_mesh(); + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; Vec3d mesh_direction = tr_inv.linear() * direction; @@ -205,49 +153,50 @@ std::optional RaycastManager::unproject(const Vec3d &point, 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); + 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) continue; - SurfacePoint surface_point(hit.position().cast(), hit.normal().cast()); - closest = Hit(key, surface_point, squared_distance); + closest = Hit{{hit.position(), hit.normal()}, key, squared_distance}; } } return closest; } -std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { - std::optional closest; +std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const +{ + std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - auto raycaster_it = std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) - continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; const Transform3d &transformation = item.second; Transform3d tr_inv = transformation.inverse(); - Vec3d mesh_point_d = tr_inv * point; - Vec3f mesh_point_f = mesh_point_d.cast(); - Vec3f n; - Vec3f p = raycaster.get_closest_point(mesh_point_f, &n); - double squared_distance = (mesh_point_f - p).squaredNorm(); + Vec3d mesh_point = tr_inv * point; + + int face_idx = 0; + Vec3d closest_point; + Vec3d pointd = point.cast(); + mesh->squared_distance(pointd, face_idx, closest_point); + + double squared_distance = (mesh_point - closest_point).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - SurfacePoint surface_point(p,n); - closest = Hit(key, surface_point, squared_distance); + + closest = ClosePoint{key, closest_point, squared_distance}; } return closest; } Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { + // TODO: transformations are sorted use lower bound auto item = std::find_if(m_transformations.begin(), m_transformations.end(), [&tr_key](const TrItem &it) -> bool { @@ -255,4 +204,78 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons }); if (item == m_transformations.end()) return Transform3d::Identity(); return item->second; -} \ No newline at end of file +} + +void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) +{ + // check if volume was removed + std::vector removed_meshes(meshes.size(), {true}); + bool need_sort = false; + // actualize MeshRaycaster + for (const ModelVolume *volume : volumes) { + size_t oid = volume->id().id; + if (skip != nullptr && skip->skip(oid)) + continue; + auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); + if (item == meshes.end()) { + // add new raycaster + bool calculate_epsilon = true; + auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); + meshes.emplace_back(std::make_pair(oid, std::move(mesh))); + need_sort = true; + } else { + size_t index = item - meshes.begin(); + removed_meshes[index] = false; + } + } + + // clean other raycasters + for (int i = removed_meshes.size() - 1; i >= 0; --i) + if (removed_meshes[i]) + meshes.erase(meshes.begin() + i); + + // All the time meshes must be sorted by volume id - for faster search + if (need_sort) { + auto is_lower = [](const RaycastManager::Mesh &m1, const RaycastManager::Mesh &m2) { return m1.first < m2.first; }; + std::sort(meshes.begin(), meshes.end(), is_lower); + } +} + +std::optional priv::unproject_on_mesh(const AABBMesh &aabb_mesh, + const Vec2d &mouse_pos, + const Transform3d &transformation, + const Camera &camera) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv * point; + direction = inv.linear() * direction; + std::vector hits = aabb_mesh.query_ray_hits(point, direction); + + if (hits.empty()) + return {}; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + return priv::SurfacePoint{hit.position(), hit.normal()}; +} + +const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) +{ + auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; + auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index); + if (it == meshes.end() || it->first != volume_id) + return nullptr; + return &(*(it->second)); +} + +RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool { + return priv::is_lower_key(it.first, key); + }; + auto it = std::lower_bound(items.begin(), items.end(), key, fnc); + if (it == items.end() || it->first != key) + return items.end(); + return it; +} diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 5451c4e92..406e51c86 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -2,12 +2,12 @@ #define slic3r_RaycastManager_hpp_ #include // unique_ptr -#include // unique_ptr -#include -#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster +#include +#include "libslic3r/AABBMesh.hpp" // Structure to cast rays #include "libslic3r/Point.hpp" // Transform3d #include "libslic3r/ObjectID.hpp" #include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume +#include "slic3r/GUI/Camera.hpp" namespace Slic3r::GUI{ @@ -17,19 +17,22 @@ namespace Slic3r::GUI{ /// class RaycastManager { - // ModelVolume.id - using Raycaster = std::pair >; - std::vector m_raycasters; +// Public structures used by RaycastManager +public: - // Key for transformation consist of unique volume and instance + // ModelVolume.id + using Mesh = std::pair >; + using Meshes = std::vector; + + // Key for transformation consist of unique volume and instance id ... ObjectId() // ModelInstance, ModelVolume using TrKey = std::pair; using TrItem = std::pair; - std::vector m_transformations; + using TrItems = std::vector; - // should contain shared pointer to camera but it is not shared pointer so it need it every time when casts rays - -public: + /// + /// Interface for identify allowed volumes to cast rays. + /// class ISkip{ public: virtual ~ISkip() = default; @@ -42,6 +45,39 @@ public: virtual bool skip(const size_t &model_volume_id) const { return false; } }; + // TODO: it is more general object move outside of this class + template + struct SurfacePoint { + using Vec3 = Eigen::Matrix; + Vec3 position = Vec3::Zero(); + Vec3 normal = Vec3::UnitZ(); + }; + + struct Hit : public SurfacePoint + { + TrKey tr_key; + double squared_distance; + }; + + struct ClosePoint + { + TrKey tr_key; + Vec3d point; + double squared_distance; + }; + +// Members +private: + // Keep structure to fast cast rays + // meshes are sorted by volume_id for faster search + Meshes m_meshes; + + // Keep transformation of meshes + TrItems m_transformations; + // Note: one mesh could have more transformations ... instances + +public: + /// /// Actualize raycasters + transformation /// Detection of removed object @@ -53,27 +89,6 @@ public: void actualize(const ModelObject *object, const ISkip *skip = nullptr); void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); - // TODO: it is more general object move outside of this class - struct SurfacePoint - { - Vec3f position = Vec3f::Zero(); - Vec3f normal = Vec3f::UnitZ(); - SurfacePoint() = default; - SurfacePoint(Vec3f position, Vec3f normal) - : position(position), normal(normal) - {} - }; - - struct Hit: public SurfacePoint - { - using Key = TrKey; - Key tr_key; - double squared_distance; - Hit(const Key& tr_key, const SurfacePoint& surface_point, double squared_distance) - : SurfacePoint(surface_point), tr_key(tr_key), squared_distance(squared_distance) - {} - }; - class SkipVolume: public ISkip { size_t volume_id; @@ -95,7 +110,6 @@ public: /// /// Unproject on mesh by Mesh raycasters - /// Note: Function use current camera position from wxGetApp() /// /// Position of mouse on screen /// Projection params @@ -121,7 +135,7 @@ public: /// Point /// Define which caster will be skipped, null mean no skip /// - std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; + std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; /// /// Getter on transformation from hitted volume to world From b82f1fe8187a7873c795b08b114869e9b22e17e2 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 08:16:54 +0100 Subject: [PATCH 024/201] Move over surface with relative transformation --- src/libslic3r/Emboss.cpp | 39 ++++++++--- src/libslic3r/Emboss.hpp | 12 +++- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 63 +++++++---------- src/slic3r/Utils/RaycastManager.cpp | 92 ++++++++++++++----------- src/slic3r/Utils/RaycastManager.hpp | 7 +- 5 files changed, 119 insertions(+), 94 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index e3f7454a7..44fa40202 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1540,8 +1540,28 @@ std::optional Emboss::ProjectZ::unproject(const Vec3d &p, double *depth) return Vec2d(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE); } -Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, - const Vec3f &normal, + +Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) +{ + // Normal must be 1 + assert(is_approx(normal.norm(), 1.)); + + // wanted up direction of result + Vec3d wanted_up_side = + (std::fabs(normal.z()) > up_limit)? + Vec3d::UnitY() : Vec3d::UnitZ(); + + // create perpendicular unit vector to surface triangle normal vector + // lay on surface of triangle and define up vector for text + Vec3d wanted_up_dir = normal.cross(wanted_up_side).cross(normal); + // normal3d is NOT perpendicular to normal_up_dir + wanted_up_dir.normalize(); + + return wanted_up_dir; +} + +Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, + const Vec3d &normal, float up_limit) { // up and emboss direction for generated model @@ -1552,28 +1572,27 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Vec3d wanted_up_side = Vec3d::UnitZ(); if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY(); - Vec3d wanted_emboss_dir = normal.cast(); // after cast from float it needs to be normalized again - wanted_emboss_dir.normalize(); + assert(is_approx(normal.norm(), 1.)); // create perpendicular unit vector to surface triangle normal vector // lay on surface of triangle and define up vector for text - Vec3d wanted_up_dir = wanted_emboss_dir + Vec3d wanted_up_dir = normal .cross(wanted_up_side) - .cross(wanted_emboss_dir); + .cross(normal); // normal3d is NOT perpendicular to normal_up_dir wanted_up_dir.normalize(); // perpendicular to emboss vector of text and normal Vec3d axis_view; double angle_view; - if (wanted_emboss_dir == -Vec3d::UnitZ()) { + if (normal == -Vec3d::UnitZ()) { // text_emboss_dir has opposit direction to wanted_emboss_dir axis_view = Vec3d::UnitY(); angle_view = M_PI; } else { - axis_view = text_emboss_dir.cross(wanted_emboss_dir); - angle_view = std::acos(text_emboss_dir.dot(wanted_emboss_dir)); // in rad + axis_view = text_emboss_dir.cross(normal); + angle_view = std::acos(text_emboss_dir.dot(normal)); // in rad axis_view.normalize(); } @@ -1593,7 +1612,7 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Eigen::AngleAxis up_rot(angle_up, text_emboss_dir); Transform3d transform = Transform3d::Identity(); - transform.translate(position.cast()); + transform.translate(position); transform.rotate(view_rot); transform.rotate(up_rot); return transform; diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index ca27afe45..cf15aa2cb 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -273,7 +273,15 @@ namespace Emboss /// Define transformation from 2d to 3d(orientation, position, scale, ...) /// Projected shape into space indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection); - + + /// + /// Suggest wanted up vector of embossed text by emboss direction + /// + /// Normalized vector of emboss direction in world + /// Is compared with normal.z to suggest up direction + /// Wanted up vector + Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9); + /// /// Create transformation for emboss text object to lay on surface point /// @@ -282,7 +290,7 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Transformation onto surface point Transform3d create_transformation_onto_surface( - const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f); + const Vec3d &position, const Vec3d &normal, float up_limit = 0.9f); class ProjectZ : public IProjection { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index c6ce9da8f..4493fba38 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -828,42 +828,38 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + auto hit = m_raycast_manager.ray_from_camera(offseted_mouse, camera, &m_surface_drag->condition); m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw m_parent.set_as_dirty(); return true; } - // Calculate offset: transformation to wanted position - Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; - Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); - - Vec3d hit_position = hit->position.cast(); - Vec3d offset_volume = hit_to_volume * hit_position; - Transform3d translate{Eigen::Translation(offset_volume)}; - - Transform3d rotate; - // normal transformed to volume - Vec3d hit_normal = hit->normal.cast(); - Vec3d z_t = hit_to_volume.linear() * hit_normal; - bool exist_rotate = priv::allign_z(z_t, rotate); - // Edit position from right - Transform3d volume_new = m_surface_drag->volume_tr * translate * rotate; const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); const Transform3d &volume = m_surface_drag->volume_tr; - Vec3d hit_position_world = hit_to_world * hit_position; - Vec3d hit_normal_world = hit_to_world.linear() * hit_normal; + // Calculate offset: transformation to wanted position + Transform3d text_to_world_old = instance * volume; + { + // Reset skew of the text Z axis: + // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. + Vec3d old_z = text_to_world_old.linear().col(2); + Vec3d new_z = text_to_world_old.linear().col(0).cross(text_to_world_old.linear().col(1)); + text_to_world_old.linear().col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + } - // REWRITE transformation - Transform3d volume_R = priv::surface_transformR(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); - volume_new = volume_R; + // normal transformed to volume + Vec3d text_z_world = text_to_world_old.linear() * Vec3d::UnitZ(); + auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); + Transform3d text_to_world_new = z_rotation * text_to_world_old; + + // Fix up vector ?? + //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + + // Edit position from right + Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; + volume_new.linear() = instance.linear().inverse() * text_to_world_new.linear(); assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) @@ -871,14 +867,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Check scale in world // Calculate Scale to keep size after move over scaled surface - Transform3d current_world = instance * volume; - auto current_world_linear = current_world.linear(); - - Transform3d wanted_world = instance * volume_new; - auto wanted_world_linear = wanted_world.linear(); - - m_surface_drag->y_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitY()); - m_surface_drag->z_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitZ()); + m_surface_drag->y_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitY()); + m_surface_drag->z_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitZ()); // recalculate rotation for scaled volume //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); @@ -4245,7 +4235,7 @@ bool priv::start_create_volume_on_surface_job( raycaster.actualize(obj, &cond); const Camera &camera = plater->get_camera(); - std::optional hit = raycaster.unproject(screen_coor, camera); + std::optional hit = raycaster.ray_from_camera(screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set @@ -4253,15 +4243,14 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - Transform3d hit_to_world = raycaster.get_transformation(hit->tr_key); // priv::reset_skew(hit_to_world); Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position.cast(), hit->normal.cast()); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d world_new = hit_to_world * surface_trmat; + Transform3d world_new = surface_trmat; // Reset skew priv::reset_skew_respect_z(world_new); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index fad41424f..3f34c37fc 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -19,14 +19,6 @@ static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager:: return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; - - // Copy functionality from MeshRaycaster::unproject_on_mesh without filtering -using SurfacePoint = RaycastManager::SurfacePoint; -static std::optional unproject_on_mesh(const AABBMesh &aabb_mesh, - const Vec2d &mouse_pos, - const Transform3d &transformation, - const Camera &camera); - } void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) @@ -108,11 +100,21 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) if (need_sort) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } - -std::optional RaycastManager::unproject( + +std::optional RaycastManager::ray_from_camera( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { - std::optional closest; + // Improve it is not neccessaru to use AABBMesh and calc normal in + + struct Result + { + const AABBMesh *mesh = nullptr; + double squared_distance; + int face; + Vec3d hit_world; + const Transform3d *tramsformation; + const TrKey *key; + }result; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -120,18 +122,46 @@ std::optional RaycastManager::unproject( const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - auto surface_point_opt = - priv::unproject_on_mesh(*mesh, mouse_pos, transformation, camera); - if (!surface_point_opt.has_value()) + + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv * point; + direction = inv.linear() * direction; + std::vector hits = mesh->query_ray_hits(point, direction); + if (hits.empty()) continue; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + + // convert to world + Vec3d hit_world = transformation * hit.position(); + double squared_distance = (camera.get_position() - hit_world).squaredNorm(); + if (result.mesh != nullptr && + result.squared_distance < squared_distance) continue; - Vec3d act_hit_tr = transformation * surface_point_opt->position.cast(); - double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); - if (closest.has_value() && - closest->squared_distance < squared_distance) - continue; - closest = Hit{*surface_point_opt, key, squared_distance}; + + result.mesh = mesh; + result.squared_distance = squared_distance; + result.face = hit.face(); + result.hit_world = hit_world; + result.tramsformation = &transformation; + result.key = &key; } - return closest; + + if (result.mesh == nullptr) + return {}; + + const Vec3i tri = result.mesh->indices(result.face); + Vec3d pts[3]; + auto tr = result.tramsformation->linear(); + for (int i = 0; i < 3; ++i) + pts[i] = tr * result.mesh->vertices(tri[i]).cast(); + Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); + normal_world.normalize(); + + SurfacePoint point_world{result.hit_world, normal_world}; + return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; } std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const @@ -241,26 +271,6 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu } } -std::optional priv::unproject_on_mesh(const AABBMesh &aabb_mesh, - const Vec2d &mouse_pos, - const Transform3d &transformation, - const Camera &camera) -{ - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - Transform3d inv = transformation.inverse(); - point = inv * point; - direction = inv.linear() * direction; - std::vector hits = aabb_mesh.query_ray_hits(point, direction); - - if (hits.empty()) - return {}; // no intersection found - - const AABBMesh::hit_result &hit = hits.front(); - return priv::SurfacePoint{hit.position(), hit.normal()}; -} - const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) { auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 406e51c86..5bd28d00a 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -114,10 +114,9 @@ public: /// Position of mouse on screen /// Projection params /// Define which caster will be skipped, null mean no skip - /// Position on surface, normal direction and transformation key, which define hitted object instance - std::optional unproject(const Vec2d &mouse_pos, - const Camera &camera, - const ISkip *skip = nullptr) const; + /// Position on surface, normal direction in world coorinate + /// + key, to know hitted instance and volume + std::optional ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const; /// /// Unproject Ray(point direction) on mesh by MeshRaycasters From 5be8e41545f8202b1b973ba1d82368f20ef8ee1e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 10:13:54 +0100 Subject: [PATCH 025/201] Clean up calculation of transformation --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 251 ++++-------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 19 +- 2 files changed, 36 insertions(+), 234 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 4493fba38..5aba10728 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -551,167 +551,6 @@ void reset_skew(Transform3d& m) m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; } -void reset_skew_respect_z(Transform3d &m) -{ - Vec3d z_before = m * Vec3d::UnitZ(); - priv::reset_skew(m); - Vec3d z_after = m * Vec3d::UnitZ(); - - Transform3d rot; // = Transform3d::Identity(); - if (priv::allign_vec(z_after, z_before, rot)) - m = m * rot; -} - -// Multiply from right -static Transform3d surface_transformR(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { - Transform3d to_volume = (i * v).inverse(); - Vec3d offset_volume = to_volume * p; - Transform3d translate{Eigen::Translation(offset_volume)}; - - // new transformation for volume - Transform3d v_new = v * translate; - - // rotation when exists - Transform3d rotate; - - // normal transformed to volume - Vec3d z_t = to_volume.linear() * n; - if (priv::allign_z(z_t, rotate)) - v_new = v_new * rotate; - - // Reset skew in world - Transform3d w_new = i * v_new; - priv::reset_skew_respect_z(w_new); - v_new = i.inverse() * w_new; - - return v_new; -} - -/// -/// Create transformation for volume to move over surface -/// Multiply from Left side - NOT WORK - with scaled instances -/// -/// Point in world coordinate -/// Normal in world coordinate - orientation -/// Original volume transformation -/// Instance transformation -/// Transformation of volume to lay on surface -static Transform3d surface_transformL(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { - // w .. original world - Transform3d w = i * v; - - // remove already existing of skew before calc rotation - // priv::reset_skew(w); - - // z .. unit z vector in world coordinate - Vec3d z = w.linear() * Vec3d::UnitZ(); - Transform3d rot = Transform3d::Identity(); - bool exist_rot = priv::allign_vec(z, n, rot); - - // rot_w .. new rotation applied on world - Transform3d rot_w = rot * w; - - // p0 .. Zero of volume in world - Vec3d p0 = rot_w * Vec3d::Zero(); - Vec3d offset = p - p0; // in world - Transform3d tr{Eigen::Translation(offset)}; - - // w2 .. wanted world transformation - Transform3d w2 = tr * rot_w; - - //priv::reset_skew_respect_z(w2); - - // _ .. inverse - // i_ .. instance inverse - Transform3d i_ = i.inverse(); - - // w = i * v \\ left multiply by i_ - // i_ * w = i_ * i * v - // v = i_ * w - return i_ * w2; - // NOTE: Do not keep scale of text when move over scaled instance -} - -// transformation inside of instance -static Transform3d surface_transform2(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) -{ - // _ .. inverse - // i_ .. instance inverse - Transform3d i_ = i.inverse(); - Vec3d pp = i_ * p; - Vec3d nn = i_.linear() * n.normalized(); - nn.normalize(); - - // z .. unit z vector in world coordinate - Vec3d z = v * Vec3d::UnitZ(); - z.normalize(); - - Transform3d rot = Transform3d::Identity(); - bool exist_rot = priv::allign_vec(z, nn, rot); - - // rot_w .. new rotation applied on world - Transform3d rotated = rot * v; - - // p0 .. Zero of volume in world - Vec3d p0 = rotated * Vec3d::Zero(); - Vec3d offset = pp - p0; // in world - Transform3d tr{Eigen::Translation(offset)}; - Transform3d volume_new = tr * rotated; - //return volume_new; - - // Remove skew in world - Transform3d world_new = i * volume_new; - reset_skew(world_new); - volume_new = i_ * world_new; - - return volume_new; -} - -// work in space defined by SVD -static Transform3d surface_transform3(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { - // w .. original world - Transform3d w = i * v; - - const Eigen::JacobiSVD svd1(w.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); - Matrix3d u1 = svd1.matrixU(); - Matrix3d v1 = svd1.matrixV(); - Matrix3d s1 = svd1.singularValues().asDiagonal(); - Transform3d tr1(Eigen::Translation3d(w.translation())); - //Transform3d test = tr1 * Transform3d(u1 * s1 * v1.transpose()); - - // modification of world - Transform3d mod(s1 * v1.transpose()); - Transform3d mod_ = mod.inverse(); - Transform3d w_mod = w * mod_; - - Vec3d nn = mod_.linear() * n; - // z .. unit z vector in world coordinate - Vec3d z = w_mod.linear() * Vec3d::UnitZ(); - Transform3d rot = Transform3d::Identity(); - bool exist_rot = priv::allign_vec(z, nn, rot); - - // rot_w .. new rotation applied on world - Transform3d rot_w = rot * w; - - // p0 .. Zero of volume in world - Vec3d p0 = rot_w * Vec3d::Zero(); - Vec3d offset = p - p0; // in world - Transform3d tr{Eigen::Translation(offset)}; - - // w2 .. wanted world transformation - Transform3d w2 = tr * rot_w; - - // _ .. inverse - // i_ .. instance inverse - Transform3d i_ = i.inverse(); - - // w = i * v \\ left multiply by i_ - // i_ * w = i_ * i * v - // v = i_ * w - return i_ * w2; - // NOTE: Do not keep scale of text when move over scaled instance -} - } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -725,15 +564,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Update surface by new position bool need_process = m_volume->text_configuration->style.prop.use_surface; - if (m_surface_drag->y_scale.has_value()) { - m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); - need_process |= set_height(); - } + //if (m_surface_drag->y_scale.has_value()) { + // m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); + // need_process |= set_height(); + //} - if (m_surface_drag->z_scale.has_value()) { - m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); - need_process |= set_depth(); - } + //if (m_surface_drag->z_scale.has_value()) { + // m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); + // need_process |= set_depth(); + //} if (need_process) process(); @@ -800,14 +639,16 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); - Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); TextConfiguration &tc = *m_volume->text_configuration; // fix baked transformation from .3mf store process if (tc.fix_3mf_tr.has_value()) volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); - m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, volume_tr, gl_volume, condition}; + Transform3d instance_tr = gl_volume->get_instance_transformation().get_matrix(); + Transform3d instance_tr_inv = instance_tr.inverse(); + Transform3d world_tr = instance_tr * volume_tr; + m_surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; // Cancel job to prevent interuption of dragging (duplicit result) if (m_job_cancel != nullptr) @@ -836,45 +677,36 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) return true; } - const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); - const Transform3d &volume = m_surface_drag->volume_tr; - + auto world_linear = m_surface_drag->world.linear(); // Calculate offset: transformation to wanted position - Transform3d text_to_world_old = instance * volume; { // Reset skew of the text Z axis: // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. - Vec3d old_z = text_to_world_old.linear().col(2); - Vec3d new_z = text_to_world_old.linear().col(0).cross(text_to_world_old.linear().col(1)); - text_to_world_old.linear().col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + Vec3d old_z = world_linear.col(2); + Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); + world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); } - // normal transformed to volume - Vec3d text_z_world = text_to_world_old.linear() * Vec3d::UnitZ(); + Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); - Transform3d text_to_world_new = z_rotation * text_to_world_old; + Transform3d world_new = z_rotation * m_surface_drag->world; + auto world_new_linear = world_new.linear(); // Fix up vector ?? - //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); // Edit position from right Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; - volume_new.linear() = instance.linear().inverse() * text_to_world_new.linear(); + volume_new.linear() = m_surface_drag->instance_inv.linear() * world_new_linear; + // Check that transformation matrix is valid transformation assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) return true; - // Check scale in world - // Calculate Scale to keep size after move over scaled surface - m_surface_drag->y_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitY()); - m_surface_drag->z_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitZ()); - - // recalculate rotation for scaled volume - //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); - //z_t = hit_to_volume2.linear() * hit->normal.cast(); - //bool exist_rotate2 = priv::allign_z(z_t, rotate); - //volume_tr = m_surface_drag->volume_tr * translate * rotate * scale; + // Check that scale in world did not changed + assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); + assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); const TextConfiguration &tc = *m_volume->text_configuration; // fix baked transformation from .3mf store process @@ -888,7 +720,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) volume_new.translate(translate); } - // Update transformation forf all instances + // Update transformation for all instances for (GLVolume *vol : m_parent.get_volumes().volumes) { if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) @@ -936,23 +768,6 @@ bool GLGizmoEmboss::on_init() std::string GLGizmoEmboss::on_get_name() const { return _u8L("Emboss"); } void GLGizmoEmboss::on_render() { - // Render debug view to surface move - if (m_surface_drag.has_value()) { - auto glvol = priv::get_gl_volume(m_parent.get_selection()); - auto tr = glvol->get_instance_transformation().get_matrix(); - CoordAxes from; - from.set_origin(m_surface_drag->from); - //from.render(tr, 2.); - - CoordAxes to; - to.set_origin(m_surface_drag->to); - //to.render(tr, 2.); - - CoordAxes axe; - axe.render(m_surface_drag->f_tr); - axe.render(m_surface_drag->t_tr); - } - // no volume selected if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr) @@ -3745,12 +3560,12 @@ void GLGizmoEmboss::draw_advanced() if (ImGui::Button(_u8L("Reset scale").c_str())) { GLVolume *gl_volume = priv::get_gl_volume(m_parent); if (gl_volume != nullptr) { - Transform3d w = gl_volume->world_matrix(); - priv::reset_skew_respect_z(w); - Transform3d i = gl_volume->get_instance_transformation().get_matrix(); - Transform3d v_new = i.inverse() * w; - gl_volume->set_volume_transformation(v_new); - m_parent.do_move(L("Reset scale")); + //Transform3d w = gl_volume->world_matrix(); + //priv::reset_skew_respect_z(w); + //Transform3d i = gl_volume->get_instance_transformation().get_matrix(); + //Transform3d v_new = i.inverse() * w; + //gl_volume->set_volume_transformation(v_new); + //m_parent.do_move(L("Reset scale")); } } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); @@ -4253,7 +4068,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d world_new = surface_trmat; // Reset skew - priv::reset_skew_respect_z(world_new); + //priv::reset_skew_respect_z(world_new); Transform3d volume_trmat = instance.inverse() * world_new; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index e1fded204..209d025fb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -317,13 +317,13 @@ private: // hold screen coor offset of cursor from object center Vec2d mouse_offset; + // Start dragging text transformations to world + Transform3d world; + // Invers transformation of text volume instance // Help convert world transformation to instance space Transform3d instance_inv; - // Start dragging volume transformation - Transform3d volume_tr; - // Dragged gl volume GLVolume *gl_volume; @@ -331,19 +331,6 @@ private: RaycastManager::AllowVolumes condition; bool exist_hit = true; - - // Visuzalization - Vec3d from = Vec3d::Zero(); - Vec3d to = Vec3d::Zero(); - - Vec3d from_dir = Vec3d::UnitZ(); - Vec3d to_dir = Vec3d::UnitZ(); - - Transform3d f_tr = Transform3d::Identity(); - Transform3d t_tr = Transform3d::Identity(); - - std::optional y_scale; - std::optional z_scale; }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; From e23c89315b50224fecbbb64364bb6062da8b567a Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 14:07:49 +0100 Subject: [PATCH 026/201] Reset for up vector --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 41 ++++++++++++------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 5aba10728..ce1c98a35 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -561,23 +561,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // write transformation from UI into model m_parent.do_move(L("Surface move")); - // Update surface by new position - bool need_process = m_volume->text_configuration->style.prop.use_surface; - - //if (m_surface_drag->y_scale.has_value()) { - // m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); - // need_process |= set_height(); - //} - - //if (m_surface_drag->z_scale.has_value()) { - // m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); - // need_process |= set_depth(); - //} - - if (need_process) + // Update surface by new position + if (m_volume->text_configuration->style.prop.use_surface) process(); - // calculate scale + // Show correct value of height & depth inside of inputs calculate_scale(); // allow moving with object again @@ -3557,15 +3545,24 @@ void GLGizmoEmboss::draw_advanced() } ImGui::SameLine(); - if (ImGui::Button(_u8L("Reset scale").c_str())) { + if (ImGui::Button(_u8L("Reset Up").c_str())) { GLVolume *gl_volume = priv::get_gl_volume(m_parent); if (gl_volume != nullptr) { - //Transform3d w = gl_volume->world_matrix(); - //priv::reset_skew_respect_z(w); - //Transform3d i = gl_volume->get_instance_transformation().get_matrix(); - //Transform3d v_new = i.inverse() * w; - //gl_volume->set_volume_transformation(v_new); - //m_parent.do_move(L("Reset scale")); + Transform3d world = gl_volume->world_matrix(); + auto world_linear = world.linear(); + Vec3d z_world = world_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = suggest_up(z_world); + + Vec3d y_world = world_linear.col(1); + auto z_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + Transform3d world_new = z_rotation * world; + auto world_new_linear = world_new.linear(); + Transform3d volume_new = gl_volume->get_volume_transformation().get_matrix(); + Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); + volume_new.linear() = instance.linear().inverse() * world_new.linear(); + gl_volume->set_volume_transformation(volume_new); + m_parent.do_move(L("Reset up vector")); } } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); From 4c321cf554661d2532412db23983d00b8d1fbc3b Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 16:49:55 +0100 Subject: [PATCH 027/201] Keep up rotation --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index ce1c98a35..45e2623e0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -680,8 +680,19 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Transform3d world_new = z_rotation * m_surface_drag->world; auto world_new_linear = world_new.linear(); - // Fix up vector ?? - //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + if (true) + { + // Fix direction of up vector + Vec3d z_world = world_new_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = suggest_up(z_world); + + Vec3d y_world = world_new_linear.col(1); + auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + + world_new = y_rotation * world_new; + world_new_linear = world_new.linear(); + } // Edit position from right Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; @@ -701,12 +712,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (tc.fix_3mf_tr.has_value()) volume_new = volume_new * (*tc.fix_3mf_tr); - // apply move in Z direction for move with flat surface above texture - const FontProp &prop = tc.style.prop; - if (!prop.use_surface && prop.distance.has_value()) { - Vec3d translate = Vec3d::UnitZ() * (*prop.distance); - volume_new.translate(translate); - } + // apply move in Z direction and rotation by up vector + apply_transformation(tc.style.prop, volume_new); // Update transformation for all instances for (GLVolume *vol : m_parent.get_volumes().volumes) { From d6b81639754c23890e3e43d2ef26efd9fc634083 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Feb 2023 12:52:08 +0100 Subject: [PATCH 028/201] Fix: drag only by text not by object Divide set and reset of text volume --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 53 +++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 1 + 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 45e2623e0..7c0186e11 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -586,13 +586,17 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; - if (m_parent.get_first_hover_volume_idx() < 0) - return false; - GLVolume *gl_volume = priv::get_gl_volume(m_parent); if (gl_volume == nullptr) return false; + // is text hovered? + const GLVolumePtrs& gl_volumes = m_parent.get_volumes().volumes; + int hovered_idx = m_parent.get_first_hover_volume_idx(); + if (hovered_idx < 0 || hovered_idx >= gl_volumes.size() || + gl_volumes[hovered_idx] != gl_volume) + return false; + // hovered object must be actual text volume const ModelObjectPtrs &objects = m_parent.get_model()->objects; if (m_volume != priv::get_model_volume(gl_volume, objects)) @@ -1005,7 +1009,7 @@ void GLGizmoEmboss::on_set_state() _u8L("ERROR: Wait until ends or Cancel process.")); return; } - set_volume(nullptr); + reset_volume(); // Store order and last activ index into app.ini // TODO: what to do when can't store into file? m_style_manager.store_styles_to_app_config(false); @@ -1015,7 +1019,7 @@ void GLGizmoEmboss::on_set_state() wxFontEnumerator::InvalidateCache(); // Try(when exist) set text configuration by volume - set_volume(priv::get_selected_volume(m_parent.get_selection())); + set_volume_by_selection(); // when open window by "T" and no valid volume is selected, so Create new one if (m_volume == nullptr || @@ -1248,25 +1252,22 @@ void GLGizmoEmboss::set_volume_by_selection() m_volume != vol) // when update volume it changed id BUT not pointer ImGuiWrapper::left_inputs(); + if (vol == nullptr) { + reset_volume(); + return; + } + // is select embossed volume? set_volume(vol); } bool GLGizmoEmboss::set_volume(ModelVolume *volume) { - if (volume == nullptr) { - if (m_volume == nullptr) - return false; - m_volume = nullptr; - // TODO: check if it is neccessary to set default text - // Idea is to set default text when create object - set_default_text(); - return false; - } + assert(volume != nullptr); const std::optional tc_opt = volume->text_configuration; if (!tc_opt.has_value()) return false; - const TextConfiguration &tc = *tc_opt; - const EmbossStyle &style = tc.style; + const TextConfiguration &tc = *tc_opt; + const EmbossStyle &style = tc.style; // Could exist OS without getter on face_name, // but it is able to restore font from descriptor @@ -1377,6 +1378,18 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) return true; } +void GLGizmoEmboss::reset_volume() +{ + if (m_volume == nullptr) + return; // already reseted + + m_volume = nullptr; + m_volume_id.id = 0; + // TODO: check if it is neccessary to set default text + // Idea is to set default text when create object + set_default_text(); +} + void GLGizmoEmboss::calculate_scale() { Transform3d to_world = m_parent.get_selection().get_first_volume()->world_matrix(); auto to_world_linear = to_world.linear(); @@ -4069,12 +4082,8 @@ bool priv::start_create_volume_on_surface_job( Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d world_new = surface_trmat; - - // Reset skew - //priv::reset_skew_respect_z(world_new); - - Transform3d volume_trmat = instance.inverse() * world_new; + // new transformation in world coor is surface_trmat + Transform3d volume_trmat = instance.inverse() * surface_trmat; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 209d025fb..f17293074 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -89,6 +89,7 @@ private: void set_volume_by_selection(); // load text configuration from volume into gizmo bool set_volume(ModelVolume *volume); + void reset_volume(); // create volume from text - main functionality bool process(); From ead192b43bc594ce9c74b564a7486e753c341b5e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Feb 2023 14:50:17 +0100 Subject: [PATCH 029/201] calculate angle when set volume --- src/libslic3r/Emboss.cpp | 62 ++++++++++++++++--------- src/libslic3r/Emboss.hpp | 8 ++++ src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 41 ++++++---------- 3 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 44fa40202..221bfbcb6 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1544,7 +1544,7 @@ std::optional Emboss::ProjectZ::unproject(const Vec3d &p, double *depth) Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) { // Normal must be 1 - assert(is_approx(normal.norm(), 1.)); + assert(is_approx(normal.squaredNorm(), 1.)); // wanted up direction of result Vec3d wanted_up_side = @@ -1560,28 +1560,45 @@ Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) return wanted_up_dir; } +std::optional Emboss::calc_up(const Transform3d &tr, double up_limit) +{ + auto tr_linear = tr.linear(); + // z base of transformation ( tr * UnitZ ) + Vec3d normal = tr_linear.col(2); + // scaled matrix has base with different size + normal.normalize(); + Vec3d suggested = suggest_up(normal); + assert(is_approx(suggested.squaredNorm(), 1.)); + + Vec3d up = tr_linear.col(1); // tr * UnitY() + up.normalize(); + + double dot = suggested.dot(up); + if (dot >= 1. || dot <= -1.) + return {}; // zero angle + + Matrix3d m; + m.row(0) = up; + m.row(1) = suggested; + m.row(2) = normal; + double det = m.determinant(); + + return atan2(det, dot); +} + Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, const Vec3d &normal, float up_limit) { - // up and emboss direction for generated model - Vec3d text_up_dir = Vec3d::UnitY(); - Vec3d text_emboss_dir = Vec3d::UnitZ(); + // is normalized ? + assert(is_approx(normal.squaredNorm(), 1.)); - // wanted up direction of result - Vec3d wanted_up_side = Vec3d::UnitZ(); - if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY(); + // up and emboss direction for generated model + Vec3d up_dir = Vec3d::UnitY(); + Vec3d emboss_dir = Vec3d::UnitZ(); // after cast from float it needs to be normalized again - assert(is_approx(normal.norm(), 1.)); - - // create perpendicular unit vector to surface triangle normal vector - // lay on surface of triangle and define up vector for text - Vec3d wanted_up_dir = normal - .cross(wanted_up_side) - .cross(normal); - // normal3d is NOT perpendicular to normal_up_dir - wanted_up_dir.normalize(); + Vec3d wanted_up_dir = suggest_up(normal, up_limit); // perpendicular to emboss vector of text and normal Vec3d axis_view; @@ -1591,25 +1608,24 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, axis_view = Vec3d::UnitY(); angle_view = M_PI; } else { - axis_view = text_emboss_dir.cross(normal); - angle_view = std::acos(text_emboss_dir.dot(normal)); // in rad + axis_view = emboss_dir.cross(normal); + angle_view = std::acos(emboss_dir.dot(normal)); // in rad axis_view.normalize(); } Eigen::AngleAxis view_rot(angle_view, axis_view); Vec3d wanterd_up_rotated = view_rot.matrix().inverse() * wanted_up_dir; wanterd_up_rotated.normalize(); - double angle_up = std::acos(text_up_dir.dot(wanterd_up_rotated)); + double angle_up = std::acos(up_dir.dot(wanterd_up_rotated)); - // text_view and text_view2 should have same direction - Vec3d text_view2 = text_up_dir.cross(wanterd_up_rotated); - Vec3d diff_view = text_emboss_dir - text_view2; + Vec3d text_view = up_dir.cross(wanterd_up_rotated); + Vec3d diff_view = emboss_dir - text_view; if (std::fabs(diff_view.x()) > 1. || std::fabs(diff_view.y()) > 1. || std::fabs(diff_view.z()) > 1.) // oposit direction angle_up *= -1.; - Eigen::AngleAxis up_rot(angle_up, text_emboss_dir); + Eigen::AngleAxis up_rot(angle_up, emboss_dir); Transform3d transform = Transform3d::Identity(); transform.translate(position); diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index cf15aa2cb..82ef4d1ac 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -281,6 +281,14 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Wanted up vector Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9); + + /// + /// By transformation calculate angle between suggested and actual up vector + /// + /// Transformation of embossed volume in world + /// Is compared with normal.z to suggest up direction + /// Rotation of suggested up-vector[in rad] in the range [-Pi, Pi], When rotation is not zero + std::optional calc_up(const Transform3d &tr, double up_limit = 0.9); /// /// Create transformation for emboss text object to lay on surface point diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 7c0186e11..b03fb6cc9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -103,8 +103,12 @@ static const struct Limits } return false; } + } limits; +// Define where is up vector on model +constexpr double up_limit = 0.9; + static bool is_text_empty(const std::string &text){ return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; @@ -1239,7 +1243,8 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } void GLGizmoEmboss::set_volume_by_selection() { - ModelVolume *vol = priv::get_selected_volume(m_parent.get_selection()); + const Selection &selection = m_parent.get_selection(); + ModelVolume *vol = priv::get_selected_volume(selection); // is same volume selected? if (vol != nullptr && vol->id() == m_volume_id) return; @@ -1259,6 +1264,13 @@ void GLGizmoEmboss::set_volume_by_selection() // is select embossed volume? set_volume(vol); + + // Check if user changed up vector by rotation or scale out of emboss gizmo + if (m_volume != nullptr) { + Transform3d world = selection.get_first_volume()->world_matrix(); + std::optional angle = calc_up(world, priv::up_limit); + m_volume->text_configuration->style.prop.angle = angle; + } } bool GLGizmoEmboss::set_volume(ModelVolume *volume) @@ -3563,31 +3575,6 @@ void GLGizmoEmboss::draw_advanced() } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str()); } - - ImGui::SameLine(); - if (ImGui::Button(_u8L("Reset Up").c_str())) { - GLVolume *gl_volume = priv::get_gl_volume(m_parent); - if (gl_volume != nullptr) { - Transform3d world = gl_volume->world_matrix(); - auto world_linear = world.linear(); - Vec3d z_world = world_linear.col(2); - z_world.normalize(); - Vec3d wanted_up = suggest_up(z_world); - - Vec3d y_world = world_linear.col(1); - auto z_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); - Transform3d world_new = z_rotation * world; - auto world_new_linear = world_new.linear(); - Transform3d volume_new = gl_volume->get_volume_transformation().get_matrix(); - Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); - volume_new.linear() = instance.linear().inverse() * world_new.linear(); - gl_volume->set_volume_transformation(volume_new); - m_parent.do_move(L("Reset up vector")); - } - } else if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); - } - #ifdef ALLOW_DEBUG_MODE ImGui::Text("family = %s", (font_prop.family.has_value() ? font_prop.family->c_str() : @@ -4079,7 +4066,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, priv::up_limit); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); // new transformation in world coor is surface_trmat From ae75599af0d1c9031c1d39436d628694beaf3aa1 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Feb 2023 12:32:53 +0100 Subject: [PATCH 030/201] clean up --- src/libslic3r/Emboss.cpp | 2 +- src/libslic3r/Emboss.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 58 +------------------------ 3 files changed, 3 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 221bfbcb6..000507779 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1588,7 +1588,7 @@ std::optional Emboss::calc_up(const Transform3d &tr, double up_limit) Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, const Vec3d &normal, - float up_limit) + double up_limit) { // is normalized ? assert(is_approx(normal.squaredNorm(), 1.)); diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 82ef4d1ac..d1ddbb1c3 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -298,7 +298,7 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Transformation onto surface point Transform3d create_transformation_onto_surface( - const Vec3d &position, const Vec3d &normal, float up_limit = 0.9f); + const Vec3d &position, const Vec3d &normal, double up_limit = 0.9); class ProjectZ : public IProjection { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b03fb6cc9..cbbe27934 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -488,44 +488,6 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu } namespace priv { -static bool allign_vec(const Vec3d &z_f, const Vec3d &z_t, Transform3d &rotate) -{ - Vec3d z_f_norm = z_f.normalized(); - Vec3d z_t_norm = z_t.normalized(); - double cos_angle = z_t_norm.dot(z_f_norm); - - // Calculate rotation of Z-vectors from current to wanted position - rotate = Transform3d::Identity(); - - if (cos_angle == 0.) { - // check that direction is not same - if (z_t_norm.z() > 0.) - return false; - - // opposit direction of z_t and z_f (a.k.a. angle 180 DEG) - rotate = Eigen::AngleAxis(M_PI, Vec3d::UnitX()); - return true; - } else if (cos_angle >= 1. || cos_angle <= -1.) { - // bad cas angle value almost zero angle so no rotation - return false; - } - - // Calculate only when angle is not zero - // Calculate rotation axe from current to wanted inside instance - Vec3d axe = z_t_norm.cross(z_f_norm); - axe.normalize(); - double angle = acos(cos_angle); - rotate = Eigen::AngleAxis(-angle, axe); - return true; -} - -static bool allign_z(const Vec3d &z_t, Transform3d &rotate) -{ - // Transformed unit vector Z direction (f)rom, (t)o - const Vec3d& z_f = Vec3d::UnitZ(); - return allign_vec(Vec3d::UnitZ(), z_t, rotate); -} - // Calculate scale in world static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) { @@ -537,24 +499,6 @@ static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to return {}; // no scale return sqrt(from_scale_sq / to_scale_sq); }; - -// Copy from branch et_transformation --> Geometry -// suggested by @bubnikv -void reset_skew(Transform3d& m) -{ - auto new_scale_factor = [](const Matrix3d& s) { - return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average - }; - - const Eigen::JacobiSVD svd(m.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); - Matrix3d u = svd.matrixU(); - Matrix3d v = svd.matrixV(); - Matrix3d s = svd.singularValues().asDiagonal(); - - //Matrix3d mirror; - m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; -} - } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -1529,7 +1473,7 @@ bool GLGizmoEmboss::process() // check that there is not unexpected volume type assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); - UpdateSurfaceVolumeData surface_data{std::move(data), text_tr, is_outside, std::move(sources)}; + UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}}; job = std::make_unique(std::move(surface_data)); } else { job = std::make_unique(std::move(data)); From 42857d8ecbcc12bf478c86473763855ed47c2d93 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Feb 2023 16:38:39 +0100 Subject: [PATCH 031/201] Use already existing AABB trees for cast into scene. Remove dependency on camera for RayCastManager. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 73 +++++++++++++++++++++---- src/slic3r/Utils/RaycastManager.cpp | 56 ++++++++++--------- src/slic3r/Utils/RaycastManager.hpp | 20 +++---- 3 files changed, 103 insertions(+), 46 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index cbbe27934..6a4aa4095 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -167,6 +167,19 @@ static void start_create_volume_job(const ModelObject *object, static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); +/// +/// Unproject on mesh by Mesh raycasters +/// +/// Position of mouse on screen +/// Projection params +/// Define which caster will be skipped, null mean no skip +/// Position on surface, normal direction in world coorinate +/// + key, to know hitted instance and volume +static std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip); + /// /// Start job for add new volume on surface of object defined by screen coor /// @@ -175,12 +188,14 @@ static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); /// Mouse position which define position /// Volume to find surface for create /// Ability to ray cast to model +/// Contain already used scene RayCasters /// True when start creation, False when there is no hit surface by screen coor static bool start_create_volume_on_surface_job(DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, - RaycastManager &raycaster); + RaycastManager &raycaster, + GLCanvas3D &canvas); /// /// Find volume in selected object with closest convex hull to screen center. @@ -235,7 +250,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); if (gl_volume != nullptr) { // Try to cast ray into scene and find object for add volume - if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager)) { + if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager, m_parent)) { // When model is broken. It could appear that hit miss the object. // So add part near by in simmilar manner as right panel do create_volume(volume_type); @@ -276,7 +291,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); if (vol == nullptr) { priv::start_create_object_job(emboss_data, screen_center); - } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) { + } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_parent)) { // in centroid of convex hull is not hit with object // soo create transfomation on border of object @@ -499,6 +514,29 @@ static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to return {}; // no scale return sqrt(from_scale_sq / to_scale_sq); }; + +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes& condition) +{ + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + const std::vector> &casters = *scene_casters; + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; + + RaycastManager::Meshes meshes; + for (const std::shared_ptr &caster : casters) { + int index = SceneRaycaster::decode_id(type, caster->get_id()); + if (index < 0 || index >= gl_volumes.size()) continue; + const GLVolume *gl_volume = gl_volumes[index]; + const ModelVolume *volume = priv::get_model_volume(gl_volume, objects); + size_t id = volume->id().id; + if (condition.skip(id)) + continue; + auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); + meshes.emplace_back(std::make_pair(id, std::move(mesh))); + } + return meshes; +} } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -568,12 +606,12 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) allowed_volumes_id.emplace_back(v->id().id); } } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + RaycastManager::Meshes meshes = priv::create_meshes(m_parent, condition); // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(instance, &condition); + m_raycast_manager.actualize(instance, &condition, &meshes); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); @@ -609,7 +647,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = m_raycast_manager.ray_from_camera(offseted_mouse, camera, &m_surface_drag->condition); + auto hit = priv::ray_from_camera(m_raycast_manager, offseted_mouse, camera, &m_surface_drag->condition); m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw @@ -3255,7 +3293,7 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); // ray in direction of text projection(from volume zero to z-dir) - std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); + std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); // Try to find closest point when no hit object in emboss direction if (!hit_opt.has_value()) { @@ -3981,8 +4019,19 @@ GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { return volumes[hovered_id]; } +std::optional priv::ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + return raycaster.first_hit(point, direction, skip); +} + bool priv::start_create_volume_on_surface_job( - DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster) + DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas) { assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; @@ -3995,10 +4044,12 @@ bool priv::start_create_volume_on_surface_job( ModelObject *obj = objects[object_idx]; size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; auto cond = RaycastManager::AllowVolumes({vol_id}); - raycaster.actualize(obj, &cond); + + RaycastManager::Meshes meshes = priv::create_meshes(canvas, cond); + raycaster.actualize(obj, &cond, &meshes); const Camera &camera = plater->get_camera(); - std::optional hit = raycaster.ray_from_camera(screen_coor, camera, &cond); + std::optional hit = priv::ray_from_camera(raycaster, screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 3f34c37fc..3b0ace2a3 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -1,16 +1,11 @@ #include "RaycastManager.hpp" #include -// include for earn camera -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/CameraUtils.hpp" - using namespace Slic3r::GUI; namespace priv { using namespace Slic3r; -static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip); +static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ return std::make_pair(instance->id().id, volume->id().id); } @@ -21,10 +16,10 @@ static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrI return is_lower_key(i1.first, i2.first); }; } -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object->volumes, skip); + priv::actualize(m_meshes, object->volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -61,11 +56,12 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { +void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, Meshes *meshes) +{ const ModelVolumePtrs &volumes = instance->get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_meshes, volumes, skip); + priv::actualize(m_meshes, volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -101,11 +97,9 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -std::optional RaycastManager::ray_from_camera( - const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const +std::optional RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const { - // Improve it is not neccessaru to use AABBMesh and calc normal in - + // Improve: it is not neccessaru to use AABBMesh and calc normal for every hit struct Result { const AABBMesh *mesh = nullptr; @@ -115,6 +109,7 @@ std::optional RaycastManager::ray_from_camera( const Transform3d *tramsformation; const TrKey *key; }result; + for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -122,24 +117,23 @@ std::optional RaycastManager::ray_from_camera( const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); Transform3d inv = transformation.inverse(); - point = inv * point; - direction = inv.linear() * direction; - std::vector hits = mesh->query_ray_hits(point, direction); + + // transform input into mesh world + Vec3d point_ = inv * point; + Vec3d direction_= inv.linear() * direction; + + std::vector hits = mesh->query_ray_hits(point_, direction_); if (hits.empty()) continue; // no intersection found const AABBMesh::hit_result &hit = hits.front(); // convert to world Vec3d hit_world = transformation * hit.position(); - double squared_distance = (camera.get_position() - hit_world).squaredNorm(); + double squared_distance = (point - hit_world).squaredNorm(); if (result.mesh != nullptr && result.squared_distance < squared_distance) - continue; + continue; // exist closer one result.mesh = mesh; result.squared_distance = squared_distance; @@ -152,6 +146,8 @@ std::optional RaycastManager::ray_from_camera( if (result.mesh == nullptr) return {}; + // Calculate normal from transformed triangle + // NOTE: Anisotropic transformation of normal is not perpendiculat to triangle const Vec3i tri = result.mesh->indices(result.face); Vec3d pts[3]; auto tr = result.tramsformation->linear(); @@ -164,7 +160,7 @@ std::optional RaycastManager::ray_from_camera( return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; } -std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const +std::optional RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const { std::optional closest; for (const auto &item : m_transformations) { @@ -236,7 +232,7 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons return item->second; } -void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) +void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) { // check if volume was removed std::vector removed_meshes(meshes.size(), {true}); @@ -248,6 +244,16 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu continue; auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); if (item == meshes.end()) { + // exist AABB in inputs ? + if (inputs != nullptr) { + auto input = std::find_if(inputs->begin(), inputs->end(), + [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); + if (input != inputs->end()) { + meshes.emplace_back(std::move(*input)); + continue; + } + } + // add new raycaster bool calculate_epsilon = true; auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 5bd28d00a..4ef9b7ca7 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -7,7 +7,6 @@ #include "libslic3r/Point.hpp" // Transform3d #include "libslic3r/ObjectID.hpp" #include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume -#include "slic3r/GUI/Camera.hpp" namespace Slic3r::GUI{ @@ -86,8 +85,9 @@ public: /// /// Model representation /// Condifiton for skip actualization - void actualize(const ModelObject *object, const ISkip *skip = nullptr); - void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); + /// Speed up for already created AABBtrees + void actualize(const ModelObject *object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); + void actualize(const ModelInstance *instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); class SkipVolume: public ISkip { @@ -109,24 +109,24 @@ public: }; /// - /// Unproject on mesh by Mesh raycasters + /// Unproject on mesh and return closest hit to point in given direction /// - /// Position of mouse on screen - /// Projection params + /// Position in space + /// Casted ray direction /// Define which caster will be skipped, null mean no skip /// Position on surface, normal direction in world coorinate /// + key, to know hitted instance and volume - std::optional ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const; + std::optional first_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; /// - /// Unproject Ray(point direction) on mesh by MeshRaycasters + /// Unproject Ray(point direction) on mesh to find closest hit of surface in given direction /// NOTE: It inspect also oposit direction of ray !! /// /// Start point for ray - /// Direction of ray + /// Direction of ray, orientation doesn't matter, both are used /// Define which caster will be skipped, null mean no skip /// Position on surface, normal direction and transformation key, which define hitted object instance - std::optional unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; + std::optional closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; /// /// Search of closest point From d3e212486bc336c883805b91f805ebc2aed74c59 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 23 Feb 2023 12:01:26 +0100 Subject: [PATCH 032/201] Try to fix conversion from uri to string path 'file://System/Library/Fonts/Helvetica.ttc#postscript-name=Helvetica' -> '/System/Library/Fonts/Helvetica.ttc' --- src/slic3r/Utils/WxFontUtils.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index a56f33141..cf1939b13 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -43,6 +43,20 @@ static bool is_valid_ttf(std::string_view file_path) return true; } +static std::string get_path_from_file_uri(const wxString &file_uri) +{ + wxURI uri(file_uri); + const wxString &path = uri.GetPath(); + BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ")."; + std::string file_path(path.c_str()); + size_t start = std::string("file://").size(); + if (file_path.empty() || file_path.size() <= start) + return {}; + // remove prefix file:// + file_path = file_path.substr(start, file_path.size() - start); + return file_path; +} + // get filepath from wxFont on Mac OsX static std::string get_file_path(const wxFont& font) { const wxNativeFontInfo *info = font.GetNativeFontInfo(); @@ -55,13 +69,7 @@ static std::string get_file_path(const wxFont& font) { if (url == NULL) return {}; wxString file_uri; wxCFTypeRef(url).GetValue(file_uri); - std::string file_path(wxURI::Unescape(file_uri).c_str()); - size_t start = std::string("file://").size(); - if (file_path.empty() || file_path.size() <= start) - return {}; - // remove prefix file:// - file_path = file_path.substr(start, file_path.size() - start); - return file_path; + return get_path_from_file_uri(file_uri); } #endif // __APPLE__ } // namespace From 650447cf021d04d31b5ca8a6b40f3d4307e3346d Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 23 Feb 2023 12:01:26 +0100 Subject: [PATCH 033/201] Second try to fix conversion from uri to string path 'file://System/Library/Fonts/Helvetica.ttc#postscript-name=Helvetica' -> '/System/Library/Fonts/Helvetica.ttc' --- src/slic3r/Utils/WxFontUtils.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index a56f33141..ebcd0d9a0 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -55,13 +55,10 @@ static std::string get_file_path(const wxFont& font) { if (url == NULL) return {}; wxString file_uri; wxCFTypeRef(url).GetValue(file_uri); - std::string file_path(wxURI::Unescape(file_uri).c_str()); - size_t start = std::string("file://").size(); - if (file_path.empty() || file_path.size() <= start) - return {}; - // remove prefix file:// - file_path = file_path.substr(start, file_path.size() - start); - return file_path; + wxURI uri(file_uri); + const wxString &path = uri.GetPath(); + BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ")."; + return std::string(path.c_str()); } #endif // __APPLE__ } // namespace From 142a21d00e4d231b9dc1703e4fabab12e9cef95b Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 23 Feb 2023 16:38:07 +0100 Subject: [PATCH 034/201] unescape uri path on MacOs --- src/slic3r/Utils/WxFontUtils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index ebcd0d9a0..2851098f2 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -57,8 +57,9 @@ static std::string get_file_path(const wxFont& font) { wxCFTypeRef(url).GetValue(file_uri); wxURI uri(file_uri); const wxString &path = uri.GetPath(); - BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ")."; - return std::string(path.c_str()); + std::string path_str(wxURI::Unescape(path).c_str()); + BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ")."; + return path_str; } #endif // __APPLE__ } // namespace From ce3785fdb1888303ce8aaac63b9adc7fa83b6894 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 11:45:24 +0100 Subject: [PATCH 035/201] Search input not change case of letter. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 6a4aa4095..c4bacf031 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2183,7 +2183,6 @@ void GLGizmoEmboss::draw_font_list() const char *popup_id = "##font_list_popup"; const char *input_id = "##font_list_input"; ImGui::SetNextItemWidth(m_gui_cfg->input_width); - ImGuiInputTextFlags input_flags = ImGuiInputTextFlags_CharsUppercase; // change color of hint to normal text bool is_popup_open = ImGui::IsPopupOpen(popup_id); @@ -2195,11 +2194,18 @@ void GLGizmoEmboss::draw_font_list() m_face_names.search.clear(); } - if (ImGui::InputTextWithHint(input_id, selected, &m_face_names.search, input_flags)) { + if (ImGui::InputTextWithHint(input_id, selected, &m_face_names.search)) { // update filtration result m_face_names.hide = std::vector(m_face_names.faces.size(), {false}); + + // search to uppercase + std::string search = m_face_names.search; // copy + std::transform(search.begin(), search.end(), search.begin(), ::toupper); + for (FaceName &face : m_face_names.faces) { - size_t index = &face - &m_face_names.faces.front(); + size_t index = &face - &m_face_names.faces.front(); + + // font name to uppercase std::string name(face.wx_name.ToUTF8().data()); std::transform(name.begin(), name.end(), name.begin(), ::toupper); From 870561469f3cd27bb140ad8b9c54079377cd37ef Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 11:47:44 +0100 Subject: [PATCH 036/201] fix --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index c4bacf031..b12aeea66 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2210,7 +2210,7 @@ void GLGizmoEmboss::draw_font_list() std::transform(name.begin(), name.end(), name.begin(), ::toupper); // It should use C++ 20 feature https://en.cppreference.com/w/cpp/string/basic_string/starts_with - bool start_with = boost::starts_with(name, m_face_names.search); + bool start_with = boost::starts_with(name, search); m_face_names.hide[index] = !start_with; } } From 9d8883036593143c4e9453f5ffca80553796c3a3 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 13:31:20 +0100 Subject: [PATCH 037/201] Separate icon manager. Create well public interface for future work and improve --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 72 ++++-------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 10 +- src/slic3r/GUI/IconManager.cpp | 150 ++++++++++++++++++++++++ src/slic3r/GUI/IconManager.hpp | 110 +++++++++++++++++ 5 files changed, 287 insertions(+), 57 deletions(-) create mode 100644 src/slic3r/GUI/IconManager.cpp create mode 100644 src/slic3r/GUI/IconManager.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 453d7eeb5..299705ce8 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -97,6 +97,8 @@ set(SLIC3R_GUI_SOURCES GUI/GUI_Geometry.hpp GUI/I18N.cpp GUI/I18N.hpp + GUI/IconManager.cpp + GUI/IconManager.hpp GUI/MainFrame.cpp GUI/MainFrame.hpp GUI/Plater.cpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b12aeea66..f6b49d1cb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3795,23 +3795,9 @@ void GLGizmoEmboss::init_icons() std::string path = resources_dir() + "/icons/"; for (std::string &filename : filenames) filename = path + filename; - // state order has to match the enum IconState - std::vector> states; - states.push_back(std::make_pair(1, false)); // Activable - states.push_back(std::make_pair(0, false)); // Hovered - states.push_back(std::make_pair(2, false)); // Disabled - - bool compress = false; - bool is_loaded = m_icons_texture.load_from_svg_files_as_sprites_array( - filenames, states, m_gui_cfg->icon_width, compress); - - if (!is_loaded || - (size_t)m_icons_texture.get_width() < (states.size() * m_gui_cfg->icon_width) || - (size_t)m_icons_texture.get_height() < (filenames.size() * m_gui_cfg->icon_width)) { - // bad load of icons, but all usage of m_icons_texture check that texture is initialized - assert(false); - m_icons_texture.reset(); - } + ImVec2 size(m_gui_cfg->icon_width, m_gui_cfg->icon_width); + auto type = IconManager::RasterType::color_wite_gray; + m_icons = m_icon_manager.init(filenames, size, type); } void GLGizmoEmboss::draw_icon(IconType icon, IconState state, ImVec2 size) @@ -3820,48 +3806,32 @@ void GLGizmoEmboss::draw_icon(IconType icon, IconState state, ImVec2 size) assert(icon != IconType::_count); if (icon == IconType::_count) return; - unsigned int icons_texture_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - // is icon loaded - if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)){ - ImGui::Text("â–®"); - return; - } - - int icon_width = m_gui_cfg->icon_width; - ImTextureID tex_id = (void *) (intptr_t) (GLuint) icons_texture_id; - int start_x = static_cast(state) * (icon_width + 1) + 1, - start_y = static_cast(icon) * (icon_width + 1) + 1; - - ImVec2 uv0(start_x / (float) tex_width, - start_y / (float) tex_height); - ImVec2 uv1((start_x + icon_width) / (float) tex_width, - (start_y + icon_width) / (float) tex_height); - - if (size.x < 1 || size.y < 1) - size = ImVec2(m_gui_cfg->icon_width, m_gui_cfg->icon_width); - - ImGui::Image(tex_id, size, uv0, uv1); + const auto &i = *m_icons[static_cast(icon)][static_cast(state)]; + IconManager::draw(i); } void GLGizmoEmboss::draw_transparent_icon() { - unsigned int icons_texture_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - // is icon loaded - if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)) { + // use top left corner of first icon + IconManager::Icon icon = *m_icons.front().front(); // copy + + if (!icon.is_valid()) { ImGui::Text("â–¯"); return; } - ImTextureID tex_id = (void *) (intptr_t) (GLuint) icons_texture_id; - int icon_width = m_gui_cfg->icon_width; - ImVec2 icon_size(icon_width, icon_width); - ImVec2 pixel_size(1.f / tex_width, 1.f / tex_height); - // zero pixel is transparent in texture - ImGui::Image(tex_id, icon_size, ImVec2(0, 0), pixel_size); + // size UV texture coors [in texture ratio] + ImVec2 size_uv( + icon.br.x-icon.tl.x, + icon.br.y-icon.tl.y); + ImVec2 one_px( + size_uv.x/icon.size.x, + size_uv.y/icon.size.y); + // reduce uv coors to one pixel + icon.br = ImVec2( + icon.tl.x + one_px.x, + icon.tl.y + one_px.y); + IconManager::draw(icon); } bool GLGizmoEmboss::draw_clickable( diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index f17293074..76d870726 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -5,15 +5,12 @@ // which overrides our localization "L" macro. #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" -#include "slic3r/GUI/GLTexture.hpp" +#include "slic3r/GUI/IconManager.hpp" #include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/Utils/EmbossStyleManager.hpp" -#include "admesh/stl.h" // indexed_triangle_set #include #include -#include -#include #include #include "libslic3r/Emboss.hpp" @@ -33,7 +30,6 @@ namespace Slic3r{ } namespace Slic3r::GUI { -class MeshRaycaster; class GLGizmoEmboss : public GLGizmoBase { public: @@ -346,7 +342,9 @@ private: void calculate_scale(); // drawing icons - GLTexture m_icons_texture; + IconManager m_icon_manager; + std::vector m_icons; + void init_icons(); enum class IconType : unsigned { rename = 0, diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp new file mode 100644 index 000000000..0cde271d0 --- /dev/null +++ b/src/slic3r/GUI/IconManager.cpp @@ -0,0 +1,150 @@ +#include "IconManager.hpp" + +#include + +using namespace Slic3r::GUI; + +namespace priv { +// set shared pointer to point on bad texture +static void clear(IconManager::Icons &icons); +static const std::vector>& get_states(IconManager::RasterType type); +} + +IconManager::~IconManager() { + priv::clear(m_icons); + // release opengl texture is made in ~GLTexture() +} + +std::vector IconManager::init(const InitTypes &input) +{ + BOOST_LOG_TRIVIAL(error) << "Not implemented yet"; + return {}; +} + +std::vector IconManager::init(const std::vector &file_paths, const ImVec2 &size, RasterType type) +{ + // TODO: remove in future + if (!m_icons.empty()) { + // not first initialization + priv::clear(m_icons); + m_icons.clear(); + m_icons_texture.reset(); + } + + // only rectangle are supported + assert(size.x == size.y); + // no subpixel supported + unsigned int width = static_cast(std::fabs(std::round(size.x))); + assert(size.x == static_cast(width)); + + // state order has to match the enum IconState + const auto& states = priv::get_states(type); + + bool compress = false; + bool is_loaded = m_icons_texture.load_from_svg_files_as_sprites_array(file_paths, states, width, compress); + if (!is_loaded || (size_t) m_icons_texture.get_width() < (states.size() * width) || + (size_t) m_icons_texture.get_height() < (file_paths.size() * width)) { + // bad load of icons, but all usage of m_icons_texture check that texture is initialized + assert(false); + m_icons_texture.reset(); + return {}; + } + + unsigned count_files = file_paths.size(); + // count icons per file + unsigned count = states.size(); + // create result + std::vector result; + result.reserve(count_files); + + Icon def_icon; + def_icon.tex_id = m_icons_texture.get_id(); + def_icon.size = size; + + // float beacouse of dividing + float tex_height = static_cast(m_icons_texture.get_height()); + float tex_width = static_cast(m_icons_texture.get_width()); + + //for (const auto &f: file_paths) { + for (unsigned f = 0; f < count_files; ++f) { + // NOTE: there are space between icons + unsigned start_y = static_cast(f) * (width + 1) + 1; + float y1 = start_y / tex_height; + float y2 = (start_y + width) / tex_height; + Icons file_icons; + file_icons.reserve(count); + //for (const auto &s : states) { + for (unsigned j = 0; j < count; ++j) { + auto icon = std::make_shared(def_icon); + // NOTE: there are space between icons + unsigned start_x = static_cast(j) * (width + 1) + 1; + float x1 = start_x / tex_width; + float x2 = (start_x + width) / tex_width; + icon->tl = ImVec2(x1, y1); + icon->br = ImVec2(x2, y2); + file_icons.push_back(icon); + m_icons.push_back(std::move(icon)); + } + result.emplace_back(std::move(file_icons)); + } + return result; +} + +void IconManager::release() { + BOOST_LOG_TRIVIAL(error) << "Not implemented yet"; +} + +void IconManager::draw(const Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col) +{ + // is icon loaded + if (!icon.is_valid()) { + ImGui::Text("?"); + return; + } + + ImTextureID id = (void *) icon.tex_id; + const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; + ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); +} + +void priv::clear(IconManager::Icons &icons) { + std::string message; + for (auto &icon : icons) { + // Exist more than this instance of shared ptr? + long count = icon.use_count(); + if (count != 1) { + // in existing icon change texture to non existing one + icon->tex_id = 0; + + std::string descr = + ((count > 2) ? (std::to_string(count - 1) + "x") : "") + // count + std::to_string(icon->size.x) + "x" + std::to_string(icon->size.y); // resolution + if (message.empty()) + message = descr; + else + message += ", " + descr; + } + } + + if (!message.empty()) + BOOST_LOG_TRIVIAL(warning) << "There is still used icons(" << message << ")."; +} + +const std::vector> &priv::get_states(IconManager::RasterType type) { + static std::vector> color = {std::make_pair(0, false)}; + static std::vector> white = {std::make_pair(1, false)}; + static std::vector> gray = {std::make_pair(2, false)}; + static std::vector> color_wite_gray = { + std::make_pair(1, false), // Activable + std::make_pair(0, false), // Hovered + std::make_pair(2, false) // Disabled + }; + + switch (type) { + case IconManager::RasterType::color: return color; + case IconManager::RasterType::white_only_data: return white; + case IconManager::RasterType::gray_only_data: return gray; + case IconManager::RasterType::color_wite_gray: return color_wite_gray; + default: return color; + } +} diff --git a/src/slic3r/GUI/IconManager.hpp b/src/slic3r/GUI/IconManager.hpp new file mode 100644 index 000000000..91177fe8a --- /dev/null +++ b/src/slic3r/GUI/IconManager.hpp @@ -0,0 +1,110 @@ +#ifndef slic3r_IconManager_hpp_ +#define slic3r_IconManager_hpp_ + +#include +#include +#include "imgui/imgui.h" // ImVec2 +#include "slic3r/GUI/GLTexture.hpp" // texture storage + +namespace Slic3r::GUI { + +/// +/// Keep texture with icons for UI +/// Manage texture live -> create and destruct texture +/// by live of icon shared pointers. +/// +class IconManager +{ +public: + /// + /// Release texture + /// Set shared pointers to invalid texture + /// + ~IconManager(); + + /// + /// Define way to convert svg data to raster + /// + enum class RasterType: int{ + color = 1 << 1, + white_only_data = 1 << 2, + gray_only_data = 1 << 3, + color_wite_gray = color | white_only_data | gray_only_data + // TODO: add type with backgrounds + }; + + struct InitType { + // path to file with image .. svg + std::string filepath; + + // resolution of stored rasterized icon + ImVec2 size; // float will be rounded + + // could contain more than one type + RasterType type = RasterType::color; + // together color, white and gray = color | white_only_data | gray_only_data + }; + using InitTypes = std::vector; + + /// + /// Data for render texture with icon + /// + struct Icon { + // stored texture size + ImVec2 size = ImVec2(-1, -1); // [in px] --> unsigned int values stored as float + + // SubTexture UV coordinate in range from 0. to 1. + ImVec2 tl; // top left -> uv0 + ImVec2 br; // bottom right -> uv1 + + // OpenGL texture id + unsigned int tex_id = 0; + bool is_valid() const { return tex_id != 0;} + // && size.x > 0 && size.y > 0 && tl.x != br.x && tl.y != br.y; + }; + using Icons = std::vector >; + + /// + /// Initialize raster texture on GPU with given images + /// NOTE: Have to be called after OpenGL initialization + /// + /// Define files and its + /// Rasterized icons stored on GPU, + /// Same size and order as input, each item of vector is set of texture in order by RasterType + std::vector init(const InitTypes &input); + + /// + /// Initialize multiple icons with same settings for size and type + /// NOTE: Have to be called after OpenGL initialization + /// + /// Define files with icon + /// Size of stored texture[in px], float will be rounded + /// Define way to rasterize icon, + /// together color, white and gray = RasterType::color | RasterType::white_only_data | RasterType::gray_only_data + /// Rasterized icons stored on GPU, + /// Same size and order as file_paths, each item of vector is set of texture in order by RasterType + std::vector init(const std::vector &file_paths, const ImVec2 &size, RasterType type = RasterType::color); + + /// + /// Release icons which are hold only by this manager + /// May change texture and position of icons. + /// + void release(); + + /// + /// Draw imgui image with icon + /// + /// Place in texture + /// [optional]Size of image, wen zero than use same size as stored texture + /// viz ImGui::Image + /// viz ImGui::Image + static void draw(const Icon &icon, const ImVec2 &size = ImVec2(0, 0), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); + +private: + // keep data stored on GPU + GLTexture m_icons_texture; + Icons m_icons; +}; + +} // namespace Slic3r::GUI +#endif // slic3r_IconManager_hpp_ \ No newline at end of file From 970cbbd1325ed7ee9b28b5f25b5d74ef670b6929 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 14:27:51 +0100 Subject: [PATCH 038/201] fix transparent icon --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index f6b49d1cb..e54deb908 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3828,9 +3828,8 @@ void GLGizmoEmboss::draw_transparent_icon() size_uv.x/icon.size.x, size_uv.y/icon.size.y); // reduce uv coors to one pixel - icon.br = ImVec2( - icon.tl.x + one_px.x, - icon.tl.y + one_px.y); + icon.tl = ImVec2(0, 0); + icon.br = one_px; IconManager::draw(icon); } From 4d0b8679ebd35d8b5f51c5e01d374066a394fbc7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 16:03:55 +0100 Subject: [PATCH 039/201] Move functionality from emboss gizmo into icon manager --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 79 ++++-------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 6 +- src/slic3r/GUI/IconManager.cpp | 80 +++++++++++++++++++++---- src/slic3r/GUI/IconManager.hpp | 38 +++++++++--- 4 files changed, 111 insertions(+), 92 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e54deb908..2992b957f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2798,7 +2798,7 @@ bool GLGizmoEmboss::draw_italic_button() const std::optional &wx_font_opt = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font_opt.has_value() || !ff.has_value()) { - draw_icon(IconType::italic, IconState::disabled); + draw(*m_icons[(int) IconType::italic][(int)IconState::disabled]); return false; } const wxFont& wx_font = *wx_font_opt; @@ -2807,8 +2807,8 @@ bool GLGizmoEmboss::draw_italic_button() bool is_font_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); if (is_font_italic) { // unset italic - if (draw_clickable(IconType::italic, IconState::hovered, - IconType::unitalic, IconState::hovered)) { + if (clickable(get_icon(IconType::italic, IconState::hovered), + get_icon(IconType::unitalic, IconState::hovered))) { skew.reset(); if (wx_font.GetStyle() != wxFontStyle::wxFONTSTYLE_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -2845,7 +2845,7 @@ bool GLGizmoEmboss::draw_bold_button() { const std::optional &wx_font_opt = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font_opt.has_value() || !ff.has_value()) { - draw_icon(IconType::bold, IconState::disabled); + draw(get_icon(IconType::bold, IconState::disabled)); return false; } const wxFont &wx_font = *wx_font_opt; @@ -2854,8 +2854,8 @@ bool GLGizmoEmboss::draw_bold_button() { bool is_font_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); if (is_font_bold) { // unset bold - if (draw_clickable(IconType::bold, IconState::hovered, - IconType::unbold, IconState::hovered)) { + if (clickable(get_icon(IconType::bold, IconState::hovered), + get_icon(IconType::unbold, IconState::hovered))) { boldness.reset(); if (wx_font.GetWeight() != wxFontWeight::wxFONTWEIGHT_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -3800,67 +3800,14 @@ void GLGizmoEmboss::init_icons() m_icons = m_icon_manager.init(filenames, size, type); } -void GLGizmoEmboss::draw_icon(IconType icon, IconState state, ImVec2 size) +const IconManager::Icon &GLGizmoEmboss::get_icon(IconType type, IconState state) { return *m_icons[(unsigned) type][(unsigned) state]; } +bool GLGizmoEmboss::draw_button(IconType type, bool disable) { - // canot draw count - assert(icon != IconType::_count); - if (icon == IconType::_count) return; - - const auto &i = *m_icons[static_cast(icon)][static_cast(state)]; - IconManager::draw(i); -} - -void GLGizmoEmboss::draw_transparent_icon() -{ - // use top left corner of first icon - IconManager::Icon icon = *m_icons.front().front(); // copy - - if (!icon.is_valid()) { - ImGui::Text("â–¯"); - return; - } - - // size UV texture coors [in texture ratio] - ImVec2 size_uv( - icon.br.x-icon.tl.x, - icon.br.y-icon.tl.y); - ImVec2 one_px( - size_uv.x/icon.size.x, - size_uv.y/icon.size.y); - // reduce uv coors to one pixel - icon.tl = ImVec2(0, 0); - icon.br = one_px; - IconManager::draw(icon); -} - -bool GLGizmoEmboss::draw_clickable( - IconType icon, IconState state, - IconType hover_icon, IconState hover_state) -{ - // check of hover - float cursor_x = ImGui::GetCursorPosX(); - draw_transparent_icon(); - ImGui::SameLine(cursor_x); - - if (ImGui::IsItemHovered()) { - // redraw image - draw_icon(hover_icon, hover_state); - } else { - // redraw normal image - draw_icon(icon, state); - } - return ImGui::IsItemClicked(); -} - -bool GLGizmoEmboss::draw_button(IconType icon, bool disable) -{ - if (disable) { - draw_icon(icon, IconState::disabled); - return false; - } - return draw_clickable( - icon, IconState::activable, - icon, IconState::hovered + return Slic3r::GUI::button( + get_icon(type, IconState::activable), + get_icon(type, IconState::hovered), + get_icon(type, IconState::disabled), + disable ); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 76d870726..2bf50ce5e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -361,10 +361,8 @@ private: // automatic calc of icon's count _count }; - enum class IconState: unsigned { activable = 0, hovered /*1*/, disabled /*2*/}; - void draw_icon(IconType icon, IconState state, ImVec2 size = ImVec2(0,0)); - void draw_transparent_icon(); - bool draw_clickable(IconType icon, IconState state, IconType hover_icon, IconState hover_state); + enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; + const IconManager::Icon& get_icon(IconType type, IconState state); bool draw_button(IconType icon, bool disable = false); // only temporary solution diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 0cde271d0..78ae673ef 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -8,6 +8,7 @@ namespace priv { // set shared pointer to point on bad texture static void clear(IconManager::Icons &icons); static const std::vector>& get_states(IconManager::RasterType type); +static void draw_transparent_icon(const IconManager::Icon &icon); // only help function } IconManager::~IconManager() { @@ -94,19 +95,6 @@ void IconManager::release() { BOOST_LOG_TRIVIAL(error) << "Not implemented yet"; } -void IconManager::draw(const Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col) -{ - // is icon loaded - if (!icon.is_valid()) { - ImGui::Text("?"); - return; - } - - ImTextureID id = (void *) icon.tex_id; - const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; - ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); -} - void priv::clear(IconManager::Icons &icons) { std::string message; for (auto &icon : icons) { @@ -148,3 +136,69 @@ const std::vector> &priv::get_states(IconManager::RasterTyp default: return color; } } + +void priv::draw_transparent_icon(const IconManager::Icon &icon) +{ + // Check input + if (!icon.is_valid()) { + assert(false); + BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon."; + ImGui::Text("?"); + return; + } + + // size UV texture coors [in texture ratio] + ImVec2 size_uv(icon.br.x - icon.tl.x, icon.br.y - icon.tl.y); + ImVec2 one_px(size_uv.x / icon.size.x, size_uv.y / icon.size.y); + + // use top left corner of first icon + IconManager::Icon icon_px = icon; // copy + // reduce uv coors to one pixel + icon_px.tl = ImVec2(0, 0); + icon_px.br = one_px; + draw(icon_px); +} + +namespace Slic3r::GUI { + +void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col) +{ + // Check input + if (!icon.is_valid()) { + assert(false); + BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon."; + ImGui::Text("?"); + return; + } + + ImTextureID id = (void *) icon.tex_id; + const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; + ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); +} + +bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover) +{ + // check of hover + float cursor_x = ImGui::GetCursorPosX(); + priv::draw_transparent_icon(icon); + ImGui::SameLine(cursor_x); + if (ImGui::IsItemHovered()) { + // redraw image + draw(icon_hover); + } else { + // redraw normal image + draw(icon); + } + return ImGui::IsItemClicked(); +} + +bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled) +{ + if (disabled) { + draw(disable); + return false; + } + return clickable(activ, hover); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/IconManager.hpp b/src/slic3r/GUI/IconManager.hpp index 91177fe8a..78b1395dc 100644 --- a/src/slic3r/GUI/IconManager.hpp +++ b/src/slic3r/GUI/IconManager.hpp @@ -91,20 +91,40 @@ public: /// void release(); - /// - /// Draw imgui image with icon - /// - /// Place in texture - /// [optional]Size of image, wen zero than use same size as stored texture - /// viz ImGui::Image - /// viz ImGui::Image - static void draw(const Icon &icon, const ImVec2 &size = ImVec2(0, 0), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); - private: // keep data stored on GPU GLTexture m_icons_texture; Icons m_icons; }; +/// +/// Draw imgui image with icon +/// +/// Place in texture +/// [optional]Size of image, wen zero than use same size as stored texture +/// viz ImGui::Image +/// viz ImGui::Image +void draw(const IconManager::Icon &icon, + const ImVec2 &size = ImVec2(0, 0), + const ImVec4 &tint_col = ImVec4(1, 1, 1, 1), + const ImVec4 &border_col = ImVec4(0, 0, 0, 0)); + +/// +/// Draw icon which change on hover +/// +/// Draw when no hover +/// Draw when hover +/// True when click, otherwise False +bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover); + +/// +/// Use icon as button with 3 states activ hover and disabled +/// +/// Not disabled not hovered image +/// Hovered image +/// Disabled image +/// True when click on enabled, otherwise False +bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled = false); + } // namespace Slic3r::GUI #endif // slic3r_IconManager_hpp_ \ No newline at end of file From e86dff528d0e23a2fe2156011821024370d042a6 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 09:17:25 +0100 Subject: [PATCH 040/201] Separate drag manager --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 90 ++++ src/slic3r/GUI/GLCanvas3D.hpp | 15 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 529 +++++++----------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 111 +---- src/slic3r/GUI/IconManager.hpp | 6 +- src/slic3r/GUI/Selection.cpp | 23 ++ src/slic3r/GUI/Selection.hpp | 4 + src/slic3r/GUI/SurfaceDrag.cpp | 244 +++++++++++ src/slic3r/GUI/SurfaceDrag.hpp | 47 +++ src/slic3r/Utils/RaycastManager.cpp | 44 ++ src/slic3r/Utils/RaycastManager.hpp | 23 ++ 12 files changed, 646 insertions(+), 492 deletions(-) create mode 100644 src/slic3r/GUI/SurfaceDrag.cpp create mode 100644 src/slic3r/GUI/SurfaceDrag.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 299705ce8..2ca1998e3 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -159,6 +159,8 @@ set(SLIC3R_GUI_SOURCES GUI/RemovableDriveManager.hpp GUI/SendSystemInfoDialog.cpp GUI/SendSystemInfoDialog.hpp + GUI/SurfaceDrag.cpp + GUI/SurfaceDrag.hpp GUI/BonjourDialog.cpp GUI/BonjourDialog.hpp GUI/ButtonsDescription.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 427c0e99f..f4573d3c4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7112,5 +7112,95 @@ const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) return ret; } +const ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects) +{ + for (const ModelObject *obj : objects) + for (const ModelVolume *vol : obj->volumes) + if (vol->id() == volume_id) + return vol; + return nullptr; +} + +ModelVolume *get_model_volume(const GLVolume &v, const ModelObject& object) { + if (v.volume_idx() < 0) + return nullptr; + + size_t volume_idx = static_cast(v.volume_idx()); + if (volume_idx >= object.volumes.size()) + return nullptr; + + return object.volumes[volume_idx]; +} + +ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects) +{ + if (v.object_idx() < 0) + return nullptr; + size_t objext_idx = static_cast(v.object_idx()); + if (objext_idx >= objects.size()) + return nullptr; + if (objects[objext_idx] == nullptr) + return nullptr; + return get_model_volume(v, *objects[objext_idx]); +} + +GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas) { + int hovered_id_signed = canvas.get_first_hover_volume_idx(); + if (hovered_id_signed < 0) + return nullptr; + + size_t hovered_id = static_cast(hovered_id_signed); + const GLVolumePtrs &volumes = canvas.get_volumes().volumes; + if (hovered_id >= volumes.size()) + return nullptr; + + return volumes[hovered_id]; +} + +GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas) { + const GLVolume *gl_volume = get_selected_gl_volume(canvas.get_selection()); + if (gl_volume == nullptr) + return nullptr; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + for (GLVolume *v : gl_volumes) + if (v->composite_id == gl_volume->composite_id) + return v; + return nullptr; +} + +ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model) { + return get_model_object(gl_volume, model.objects); +} + +ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects) { + if (gl_volume.object_idx() < 0) + return nullptr; + size_t objext_idx = static_cast(gl_volume.object_idx()); + if (objext_idx >= objects.size()) + return nullptr; + return objects[objext_idx]; +} + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model& model) { + return get_model_instance(gl_volume, model.objects); +} + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects) { + if (gl_volume.instance_idx() < 0) + return nullptr; + ModelObject *object = get_model_object(gl_volume, objects); + return get_model_instance(gl_volume, *object); +} + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object) { + if (gl_volume.instance_idx() < 0) + return nullptr; + size_t instance_idx = static_cast(gl_volume.instance_idx()); + if (instance_idx >= object.instances.size()) + return nullptr; + return object.instances[instance_idx]; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f8f5a0efc..573ef879a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1056,7 +1056,20 @@ private: float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } }; -const ModelVolume * get_model_volume(const GLVolume &v, const Model &model); +const ModelVolume *get_model_volume(const GLVolume &v, const Model &model); +const ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects); +ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects); +ModelVolume *get_model_volume(const GLVolume &v, const ModelObject &object); + +GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas); +GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas); + +ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model); +ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects); + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model &model); +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects); +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 2992b957f..14e6c7aa1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -18,7 +18,7 @@ // TODO: remove include #include "libslic3r/SVG.hpp" // debug store #include "libslic3r/Geometry.hpp" // covex hull 2d -#include "libslic3r/Timer.hpp" // covex hull 2d +#include "libslic3r/Timer.hpp" #include "libslic3r/NSVGUtils.hpp" #include "libslic3r/Model.hpp" @@ -109,11 +109,6 @@ static const struct Limits // Define where is up vector on model constexpr double up_limit = 0.9; -static bool is_text_empty(const std::string &text){ - return text.empty() || - text.find_first_not_of(" \n\t\r") == std::string::npos; -} - // Normalize radian angle from -PI to PI template void to_range_pi_pi(T& angle) { @@ -123,6 +118,63 @@ template void to_range_pi_pi(T& angle) } } } // namespace priv +using namespace priv; + +// This configs holds GUI layout size given by translated texts. +// etc. When language changes, GUI is recreated and this class constructed again, +// so the change takes effect. (info by GLGizmoFdmSupports.hpp) +struct GLGizmoEmboss::GuiCfg +{ + // Detect invalid config values when change monitor DPI + double screen_scale; + float main_toolbar_height; + + // Zero means it is calculated in init function + ImVec2 minimal_window_size = ImVec2(0, 0); + ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); + ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); + float height_of_volume_type_selector = 0.f; + float input_width = 0.f; + float delete_pos_x = 0.f; + float max_style_name_width = 0.f; + unsigned int icon_width = 0; + + // maximal width and height of style image + Vec2i max_style_image_size = Vec2i(0, 0); + + float indent = 0.f; + float input_offset = 0.f; + float advanced_input_offset = 0.f; + + ImVec2 text_size; + + // maximal size of face name image + Vec2i face_name_size = Vec2i(100, 0); + float face_name_max_width = 100.f; + float face_name_texture_offset_x = 105.f; + + // maximal texture generate jobs running at once + unsigned int max_count_opened_font_files = 10; + + // Only translations needed for calc GUI size + struct Translations + { + std::string font; + std::string size; + std::string depth; + std::string use_surface; + + // advanced + std::string char_gap; + std::string line_gap; + std::string boldness; + std::string italic; + std::string surface_distance; + std::string angle; + std::string collection; + }; + Translations translations; +}; GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) : GLGizmoBase(parent, M_ICON_FILENAME, -2) @@ -141,7 +193,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) // Private namespace with helper function for create volume namespace priv { - /// /// Prepare data for emboss /// @@ -165,21 +216,6 @@ static void start_create_volume_job(const ModelObject *object, DataBase &emboss_data, ModelVolumeType volume_type); -static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); - -/// -/// Unproject on mesh by Mesh raycasters -/// -/// Position of mouse on screen -/// Projection params -/// Define which caster will be skipped, null mean no skip -/// Position on surface, normal direction in world coorinate -/// + key, to know hitted instance and volume -static std::optional ray_from_camera(const RaycastManager &raycaster, - const Vec2d &mouse_pos, - const Camera &camera, - const RaycastManager::ISkip *skip); - /// /// Start job for add new volume on surface of object defined by screen coor /// @@ -220,13 +256,26 @@ static void find_closest_volume(const Selection &selection, /// Screen coordinat, where to create new object laying on bed static void start_create_object_job(DataBase &emboss_data, const Vec2d &coor); -/// -/// Search if exist model volume for given id in object lists -/// -/// List to search volume -/// Unique Identifier of volume -/// Volume when found otherwise nullptr -static const ModelVolume *get_volume(const ModelObjectPtrs &objects, const ObjectID &volume_id); +// Have to match order of files in function GLGizmoEmboss::init_icons() +enum class IconType : unsigned { + rename = 0, + erase, + add, + save, + undo, + italic, + unitalic, + bold, + unbold, + system_selector, + open_file, + // automatic calc of icon's count + _count +}; +// Define rendered version of icon +enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; +const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType type, IconState state); +bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); } // namespace priv @@ -246,7 +295,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous m_style_manager.discard_style_changes(); set_default_text(); - GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); + GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); if (gl_volume != nullptr) { // Try to cast ray into scene and find object for add volume @@ -353,33 +402,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) } namespace priv { - -/// -/// Access to model from gl_volume -/// TODO: it is more general function --> move to utils -/// -/// Volume to model belongs to -/// Object containing gl_volume -/// Model for volume -static ModelVolume *get_model_volume(const GLVolume *gl_volume, const ModelObject *object); - -/// -/// Access to model from gl_volume -/// TODO: it is more general function --> move to utils -/// -/// Volume to model belongs to -/// All objects -/// Model for volume -static ModelVolume *get_model_volume(const GLVolume *gl_volume, const ModelObjectPtrs &objects); - -/// -/// Access to model by selection -/// TODO: it is more general function --> move to select utils -/// -/// Actual selection -/// Model from selection -static ModelVolume *get_selected_volume(const Selection &selection); - /// /// Calculate offset from mouse position to center of text /// @@ -388,14 +410,6 @@ static ModelVolume *get_selected_volume(const Selection &selection); /// Offset in screan coordinate static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVolume &mv); -/// -/// Access to one selected volume -/// -/// Containe what is selected -/// Slected when only one volume otherwise nullptr -static const GLVolume *get_gl_volume(const Selection &selection); -static GLVolume *get_gl_volume(const GLCanvas3D &canvas); - /// /// Get transformation to world /// - use fix after store to 3mf when exists @@ -414,28 +428,6 @@ static Transform3d world_matrix(const Selection &selection); static void change_window_position(std::optional &output_window_offset, bool try_to_fix); } // namespace priv -const GLVolume *priv::get_gl_volume(const Selection &selection) { - // return selection.get_first_volume(); - const auto &list = selection.get_volume_idxs(); - if (list.size() != 1) - return nullptr; - unsigned int volume_idx = *list.begin(); - return selection.get_volume(volume_idx); -} - -GLVolume *priv::get_gl_volume(const GLCanvas3D &canvas) { - const GLVolume *gl_volume = get_gl_volume(canvas.get_selection()); - if (gl_volume == nullptr) - return nullptr; - - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - for (GLVolume *v : gl_volumes) - if (v->composite_id == gl_volume->composite_id) - return v; - - return nullptr; -} - Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) { if (!gl_volume) @@ -444,7 +436,8 @@ Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) if (!model) return res; - ModelVolume* mv = get_model_volume(gl_volume, model->objects); + + const ModelVolume* mv = get_model_volume(*gl_volume, model->objects); if (!mv) return res; @@ -461,7 +454,7 @@ Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) Transform3d priv::world_matrix(const Selection &selection) { - const GLVolume *gl_volume = get_gl_volume(selection); + const GLVolume *gl_volume = get_selected_gl_volume(selection); return world_matrix(gl_volume, selection.get_model()); } @@ -502,231 +495,47 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu return nearest_offset; } -namespace priv { - // Calculate scale in world -static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) -{ - Vec3d from_dir = from * dir; - Vec3d to_dir = to * dir; - double from_scale_sq = from_dir.squaredNorm(); - double to_scale_sq = to_dir.squaredNorm(); - if (is_approx(from_scale_sq, to_scale_sq, 1e-3)) - return {}; // no scale - return sqrt(from_scale_sq / to_scale_sq); -}; - -RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes& condition) -{ - SceneRaycaster::EType type = SceneRaycaster::EType::Volume; - auto scene_casters = canvas.get_raycasters_for_picking(type); - const std::vector> &casters = *scene_casters; - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - const ModelObjectPtrs &objects = canvas.get_model()->objects; - - RaycastManager::Meshes meshes; - for (const std::shared_ptr &caster : casters) { - int index = SceneRaycaster::decode_id(type, caster->get_id()); - if (index < 0 || index >= gl_volumes.size()) continue; - const GLVolume *gl_volume = gl_volumes[index]; - const ModelVolume *volume = priv::get_model_volume(gl_volume, objects); - size_t id = volume->id().id; - if (condition.skip(id)) - continue; - auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); - meshes.emplace_back(std::make_pair(id, std::move(mesh))); - } - return meshes; -} -} - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { - // Fix when leave window during dragging - // Fix when click right button - if (m_surface_drag.has_value() && !mouse_event.Dragging()) { - // write transformation from UI into model - m_parent.do_move(L("Surface move")); + // exist selected volume? + if (m_volume == nullptr) + return false; + + const Camera &camera = wxGetApp().plater()->get_camera(); + bool was_dragging = m_surface_drag.has_value(); + bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager); + bool is_dragging = m_surface_drag.has_value(); - // Update surface by new position + // End with surface dragging? + if (was_dragging && !is_dragging) { + // Update surface by new position if (m_volume->text_configuration->style.prop.use_surface) process(); // Show correct value of height & depth inside of inputs calculate_scale(); - - // allow moving with object again - m_parent.enable_moving(true); - m_surface_drag.reset(); - - // only left up is correct - // otherwise it is fix state and return false - return mouse_event.LeftUp(); } - if (mouse_event.Moving()) - return false; - - // detect start text dragging - if (mouse_event.LeftDown()) { - // exist selected volume? - if (m_volume == nullptr) - return false; - - GLVolume *gl_volume = priv::get_gl_volume(m_parent); - if (gl_volume == nullptr) - return false; - - // is text hovered? - const GLVolumePtrs& gl_volumes = m_parent.get_volumes().volumes; - int hovered_idx = m_parent.get_first_hover_volume_idx(); - if (hovered_idx < 0 || hovered_idx >= gl_volumes.size() || - gl_volumes[hovered_idx] != gl_volume) - return false; - - // hovered object must be actual text volume - const ModelObjectPtrs &objects = m_parent.get_model()->objects; - if (m_volume != priv::get_model_volume(gl_volume, objects)) - return false; - - const ModelInstancePtrs instances = m_volume->get_object()->instances; - int instance_id = gl_volume->instance_idx(); - if (instance_id < 0 || static_cast(instance_id) >= instances.size()) - return false; // should not happen - const ModelInstance *instance = instances[instance_id]; - - const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; - std::vector allowed_volumes_id; - if (volumes.size() > 1) { - allowed_volumes_id.reserve(volumes.size() - 1); - for (auto &v : volumes) { - if (v->id() == m_volume->id()) - continue; - if (!v->is_model_part()) - continue; - allowed_volumes_id.emplace_back(v->id().id); - } - } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - RaycastManager::Meshes meshes = priv::create_meshes(m_parent, condition); - // initialize raycasters - // INFO: It could slows down for big objects - // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(instance, &condition, &meshes); - - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); - Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); - TextConfiguration &tc = *m_volume->text_configuration; - // fix baked transformation from .3mf store process - if (tc.fix_3mf_tr.has_value()) - volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); - - Transform3d instance_tr = gl_volume->get_instance_transformation().get_matrix(); - Transform3d instance_tr_inv = instance_tr.inverse(); - Transform3d world_tr = instance_tr * volume_tr; - m_surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; - + // Start with dragging + else if (!was_dragging && is_dragging) { // Cancel job to prevent interuption of dragging (duplicit result) - if (m_job_cancel != nullptr) + if (m_job_cancel != nullptr) m_job_cancel->store(true); - - // disable moving with object by mouse - m_parent.enable_moving(false); - return true; } - // Dragging starts out of window - if (!m_surface_drag.has_value()) - return false; - - if (mouse_event.Dragging()) { - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; - const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = priv::ray_from_camera(m_raycast_manager, offseted_mouse, camera, &m_surface_drag->condition); - m_surface_drag->exist_hit = hit.has_value(); - if (!hit.has_value()) { - // cross hair need redraw - m_parent.set_as_dirty(); - return true; - } - - auto world_linear = m_surface_drag->world.linear(); - // Calculate offset: transformation to wanted position - { - // Reset skew of the text Z axis: - // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. - Vec3d old_z = world_linear.col(2); - Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); - world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); - } - - Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() - auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); - Transform3d world_new = z_rotation * m_surface_drag->world; - auto world_new_linear = world_new.linear(); - - if (true) - { - // Fix direction of up vector - Vec3d z_world = world_new_linear.col(2); - z_world.normalize(); - Vec3d wanted_up = suggest_up(z_world); - - Vec3d y_world = world_new_linear.col(1); - auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); - - world_new = y_rotation * world_new; - world_new_linear = world_new.linear(); - } - - // Edit position from right - Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; - volume_new.linear() = m_surface_drag->instance_inv.linear() * world_new_linear; - - // Check that transformation matrix is valid transformation - assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN - if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) - return true; - - // Check that scale in world did not changed - assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); - assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); - - const TextConfiguration &tc = *m_volume->text_configuration; - // fix baked transformation from .3mf store process - if (tc.fix_3mf_tr.has_value()) - volume_new = volume_new * (*tc.fix_3mf_tr); - - // apply move in Z direction and rotation by up vector - apply_transformation(tc.style.prop, volume_new); - - // Update transformation for all instances - for (GLVolume *vol : m_parent.get_volumes().volumes) { - if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || - vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) - continue; - vol->set_volume_transformation(volume_new); - } - + // during drag + else if (was_dragging && is_dragging) { // update scale of selected volume --> should be approx the same calculate_scale(); - - m_parent.set_as_dirty(); - return true; } - return false; + return res; } bool GLGizmoEmboss::on_mouse(const wxMouseEvent &mouse_event) { // not selected volume if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || !m_volume->text_configuration.has_value()) return false; if (on_mouse_for_rotation(mouse_event)) return true; @@ -755,7 +564,7 @@ std::string GLGizmoEmboss::on_get_name() const { return _u8L("Emboss"); } void GLGizmoEmboss::on_render() { // no volume selected if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr) + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr) return; Selection &selection = m_parent.get_selection(); if (selection.is_empty()) return; @@ -861,7 +670,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) set_volume_by_selection(); // Do not render window for not selected text volume if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || !m_volume->text_configuration.has_value()) { close(); return; @@ -870,15 +679,15 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); - if (!m_gui_cfg.has_value() || // Exist configuration - first run + if (m_gui_cfg == nullptr || // Exist configuration - first run m_gui_cfg->screen_scale != screen_scale || // change of DPI m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port ) { // Create cache for gui offsets - GuiCfg cfg = create_gui_configuration(); + GuiCfg cfg = create_gui_configuration(); cfg.screen_scale = screen_scale; cfg.main_toolbar_height = main_toolbar_height; - m_gui_cfg.emplace(std::move(cfg)); + m_gui_cfg = std::make_unique(std::move(cfg)); // set position near toolbar m_set_window_offset = ImVec2(-1.f, -1.f); @@ -1009,13 +818,13 @@ void GLGizmoEmboss::on_set_state() // when open window by "T" and no valid volume is selected, so Create new one if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr ) { + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr ) { // reopen gizmo when new object is created GLGizmoBase::m_state = GLGizmoBase::Off; if (wxGetApp().get_mode() == comSimple) // It's impossible to add a part in simple mode return; - // start creating new object + // start creating new object create_volume(ModelVolumeType::MODEL_PART); } @@ -1023,7 +832,7 @@ void GLGizmoEmboss::on_set_state() if (m_allow_open_near_volume) { m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); } else { - if (m_gui_cfg.has_value()) + if (m_gui_cfg != nullptr) priv::change_window_position(m_set_window_offset, false); else m_set_window_offset = ImVec2(-1, -1); @@ -1226,7 +1035,7 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } void GLGizmoEmboss::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); - ModelVolume *vol = priv::get_selected_volume(selection); + ModelVolume *vol = get_selected_volume(selection); // is same volume selected? if (vol != nullptr && vol->id() == m_volume_id) return; @@ -1351,7 +1160,7 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) // The change of volume could show or hide part with setter on volume type if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || (m_volume->get_object()->volumes.size() == 1) != (volume->get_object()->volumes.size() == 1)){ m_should_set_minimal_windows_size = true; @@ -1409,35 +1218,6 @@ void GLGizmoEmboss::calculate_scale() { m_style_manager.clear_imgui_font(); } -ModelVolume *priv::get_model_volume(const GLVolume *gl_volume, const ModelObject *object) -{ - int volume_id = gl_volume->volume_idx(); - if (volume_id < 0 || static_cast(volume_id) >= object->volumes.size()) return nullptr; - return object->volumes[volume_id]; -} - -ModelVolume *priv::get_model_volume(const GLVolume *gl_volume, const ModelObjectPtrs &objects) -{ - int object_id = gl_volume->object_idx(); - if (object_id < 0 || static_cast(object_id) >= objects.size()) return nullptr; - return get_model_volume(gl_volume, objects[object_id]); -} - -ModelVolume *priv::get_selected_volume(const Selection &selection) -{ - int object_idx = selection.get_object_idx(); - // is more object selected? - if (object_idx == -1) return nullptr; - - auto volume_idxs = selection.get_volume_idxs(); - // is more volumes selected? - if (volume_idxs.size() != 1) return nullptr; - unsigned int vol_id_gl = *volume_idxs.begin(); - const GLVolume *vol_gl = selection.get_volume(vol_id_gl); - const ModelObjectPtrs &objects = selection.get_model()->objects; - return get_model_volume(vol_gl, objects); -} - // Run Job on main thread (blocking) - ONLY DEBUG static inline void execute_job(std::shared_ptr j) { @@ -1529,6 +1309,10 @@ bool GLGizmoEmboss::process() return true; } +namespace priv { +static bool is_text_empty(const std::string &text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; } +} + void GLGizmoEmboss::close() { // remove volume when text is empty @@ -1579,10 +1363,10 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { if (is_approx(cam_dir_tr, emboss_dir)) return false; assert(sel.get_volume_idxs().size() == 1); - GLVolume *vol = sel.get_volume(*sel.get_volume_idxs().begin()); + GLVolume *gl_volume = sel.get_volume(*sel.get_volume_idxs().begin()); Transform3d vol_rot; - Transform3d vol_tr = vol->get_volume_transformation().get_matrix(); + Transform3d vol_tr = gl_volume->get_volume_transformation().get_matrix(); // check whether cam_dir is opposit to emboss dir if (is_approx(cam_dir_tr, -emboss_dir)) { // rotate 180 DEG by y @@ -1602,8 +1386,8 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { vol_rot * Eigen::Translation(offset_inv); //Transform3d res = vol_tr * vol_rot; - vol->set_volume_transformation(Geometry::Transformation(res)); - priv::get_model_volume(vol, sel.get_model()->objects)->set_transformation(res); + gl_volume->set_volume_transformation(Geometry::Transformation(res)); + get_model_volume(*gl_volume, sel.get_model()->objects)->set_transformation(res); return true; } @@ -2467,7 +2251,7 @@ void GLGizmoEmboss::draw_style_rename_button() bool can_rename = m_style_manager.exist_stored_style(); std::string title = _u8L("Rename style"); const char * popup_id = title.c_str(); - if (draw_button(IconType::rename, !can_rename)) { + if (priv::draw_button(m_icons, IconType::rename, !can_rename)) { assert(m_style_manager.get_stored_style()); ImGui::OpenPopup(popup_id); } @@ -2484,7 +2268,7 @@ void GLGizmoEmboss::draw_style_rename_button() void GLGizmoEmboss::draw_style_save_button(bool is_modified) { - if (draw_button(IconType::save, !is_modified)) { + if (draw_button(m_icons, IconType::save, !is_modified)) { // save styles to app config m_style_manager.store_styles_to_app_config(); }else if (ImGui::IsItemHovered()) { @@ -2554,7 +2338,7 @@ void GLGizmoEmboss::draw_style_add_button() const char *popup_id = title.c_str(); // save as new style ImGui::SameLine(); - if (draw_button(IconType::add, !can_add)) { + if (draw_button(m_icons, IconType::add, !can_add)) { if (!m_style_manager.exist_stored_style()) { m_style_manager.store_styles_to_app_config(wxGetApp().app_config); } else { @@ -2585,7 +2369,7 @@ void GLGizmoEmboss::draw_delete_style_button() { std::string title = _u8L("Remove style"); const char * popup_id = title.c_str(); static size_t next_style_index = std::numeric_limits::max(); - if (draw_button(IconType::erase, !can_delete)) { + if (draw_button(m_icons, IconType::erase, !can_delete)) { while (true) { // NOTE: can't use previous loaded activ index -> erase could change index size_t active_index = m_style_manager.get_style_index(); @@ -2807,8 +2591,8 @@ bool GLGizmoEmboss::draw_italic_button() bool is_font_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); if (is_font_italic) { // unset italic - if (clickable(get_icon(IconType::italic, IconState::hovered), - get_icon(IconType::unitalic, IconState::hovered))) { + if (clickable(get_icon(m_icons, IconType::italic, IconState::hovered), + get_icon(m_icons, IconType::unitalic, IconState::hovered))) { skew.reset(); if (wx_font.GetStyle() != wxFontStyle::wxFONTSTYLE_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -2822,7 +2606,7 @@ bool GLGizmoEmboss::draw_italic_button() ImGui::SetTooltip("%s", _u8L("Unset italic").c_str()); } else { // set italic - if (draw_button(IconType::italic)) { + if (draw_button(m_icons, IconType::italic)) { wxFont new_wx_font = wx_font; // copy auto new_ff = WxFontUtils::set_italic(new_wx_font, *ff.font_file); if (new_ff != nullptr) { @@ -2845,7 +2629,7 @@ bool GLGizmoEmboss::draw_bold_button() { const std::optional &wx_font_opt = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font_opt.has_value() || !ff.has_value()) { - draw(get_icon(IconType::bold, IconState::disabled)); + draw(get_icon(m_icons, IconType::bold, IconState::disabled)); return false; } const wxFont &wx_font = *wx_font_opt; @@ -2854,8 +2638,8 @@ bool GLGizmoEmboss::draw_bold_button() { bool is_font_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); if (is_font_bold) { // unset bold - if (clickable(get_icon(IconType::bold, IconState::hovered), - get_icon(IconType::unbold, IconState::hovered))) { + if (clickable(get_icon(m_icons, IconType::bold, IconState::hovered), + get_icon(m_icons, IconType::unbold, IconState::hovered))) { boldness.reset(); if (wx_font.GetWeight() != wxFontWeight::wxFONTWEIGHT_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -2869,7 +2653,7 @@ bool GLGizmoEmboss::draw_bold_button() { ImGui::SetTooltip("%s", _u8L("Unset bold").c_str()); } else { // set bold - if (draw_button(IconType::bold)) { + if (draw_button(m_icons, IconType::bold)) { wxFont new_wx_font = wx_font; // copy auto new_ff = WxFontUtils::set_bold(new_wx_font, *ff.font_file); if (new_ff != nullptr) { @@ -2922,7 +2706,7 @@ bool GLGizmoEmboss::revertible(const std::string &name, // render revert changes button if (changed) { ImGui::SameLine(undo_offset); - if (draw_button(IconType::undo)) { + if (draw_button(m_icons, IconType::undo)) { value = *default_value; return true; } else if (ImGui::IsItemHovered()) @@ -3074,7 +2858,7 @@ void GLGizmoEmboss::draw_style_edit() { EmbossStyle &style = m_style_manager.get_style(); if (exist_change_in_font) { ImGui::SameLine(ImGui::GetStyle().FramePadding.x); - if (draw_button(IconType::undo)) { + if (draw_button(m_icons, IconType::undo)) { const EmbossStyle *stored_style = m_style_manager.get_stored_style(); style.path = stored_style->path; style.prop.boldness = stored_style->prop.boldness; @@ -3293,7 +3077,7 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas raycast_manager.actualize(volume.get_object(), &cond); //const Selection &selection = m_parent.get_selection(); - const GLVolume *gl_volume = priv::get_gl_volume(selection); + const GLVolume *gl_volume = get_selected_gl_volume(selection); Transform3d to_world = priv::world_matrix(gl_volume, selection.get_model()); Vec3d point = to_world * Vec3d::Zero(); Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); @@ -3555,7 +3339,7 @@ void GLGizmoEmboss::draw_advanced() } if (ImGui::Button(_u8L("Set text to face camera").c_str())) { - assert(priv::get_selected_volume(m_parent.get_selection()) == m_volume); + assert(get_selected_volume(m_parent.get_selection()) == m_volume); const Camera &cam = wxGetApp().plater()->get_camera(); bool use_surface = m_style_manager.get_style().prop.use_surface; if (priv::apply_camera_dir(cam, m_parent) && use_surface) @@ -3800,13 +3584,13 @@ void GLGizmoEmboss::init_icons() m_icons = m_icon_manager.init(filenames, size, type); } -const IconManager::Icon &GLGizmoEmboss::get_icon(IconType type, IconState state) { return *m_icons[(unsigned) type][(unsigned) state]; } -bool GLGizmoEmboss::draw_button(IconType type, bool disable) +const IconManager::Icon &priv::get_icon(const IconManager::VIcons& icons, IconType type, IconState state) { return *icons[(unsigned) type][(unsigned) state]; } +bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool disable) { return Slic3r::GUI::button( - get_icon(type, IconState::activable), - get_icon(type, IconState::hovered), - get_icon(type, IconState::disabled), + get_icon(icons, type, IconState::activable), + get_icon(icons, type, IconState::hovered), + get_icon(icons, type, IconState::disabled), disable ); } @@ -3921,37 +3705,6 @@ void priv::start_create_volume_job(const ModelObject *object, queue_job(worker, std::move(job)); } -const ModelVolume *priv::get_volume(const ModelObjectPtrs &objects, const ObjectID &volume_id) -{ - for (const ModelObject *obj : objects) - for (const ModelVolume *vol : obj->volumes) - if (vol->id() == volume_id) - return vol; - return nullptr; -}; - -GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { - int hovered_id_signed = canvas.get_first_hover_volume_idx(); - if (hovered_id_signed < 0) return nullptr; - - size_t hovered_id = static_cast(hovered_id_signed); - const GLVolumePtrs &volumes = canvas.get_volumes().volumes; - if (hovered_id >= volumes.size()) return nullptr; - - return volumes[hovered_id]; -} - -std::optional priv::ray_from_camera(const RaycastManager &raycaster, - const Vec2d &mouse_pos, - const Camera &camera, - const RaycastManager::ISkip *skip) -{ - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - return raycaster.first_hit(point, direction, skip); -} - bool priv::start_create_volume_on_surface_job( DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas) { @@ -3967,11 +3720,11 @@ bool priv::start_create_volume_on_surface_job( size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; auto cond = RaycastManager::AllowVolumes({vol_id}); - RaycastManager::Meshes meshes = priv::create_meshes(canvas, cond); + RaycastManager::Meshes meshes = create_meshes(canvas, cond); raycaster.actualize(obj, &cond, &meshes); const Camera &camera = plater->get_camera(); - std::optional hit = priv::ray_from_camera(raycaster, screen_coor, camera, &cond); + std::optional hit = ray_from_camera(raycaster, screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set @@ -4009,7 +3762,7 @@ void priv::find_closest_volume(const Selection &selection, double center_sq_distance = std::numeric_limits::max(); for (unsigned int id : indices) { const GLVolume *gl_volume = selection.get_volume(id); - ModelVolume *volume = priv::get_model_volume(gl_volume, objects); + const ModelVolume *volume = get_model_volume(*gl_volume, objects); if (!volume->is_model_part()) continue; Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume); Vec2d c = hull.centroid().cast(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 2bf50ce5e..5c5a63ebe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -6,6 +6,7 @@ #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" #include "slic3r/GUI/IconManager.hpp" +#include "slic3r/GUI/SurfaceDrag.hpp" #include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/Utils/EmbossStyleManager.hpp" @@ -25,7 +26,6 @@ class wxFont; namespace Slic3r{ class AppConfig; class GLVolume; - enum class ModelVolumeType : int; } @@ -145,7 +145,7 @@ private: template bool revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw); - bool m_should_set_minimal_windows_size = false; + bool m_should_set_minimal_windows_size = false; void set_minimal_window_size(bool is_advance_edit_style); ImVec2 get_minimal_window_size() const; @@ -161,65 +161,12 @@ private: bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); void remove_notification_not_valid_font(); - - // This configs holds GUI layout size given by translated texts. - // etc. When language changes, GUI is recreated and this class constructed again, - // so the change takes effect. (info by GLGizmoFdmSupports.hpp) - struct GuiCfg - { - // Detect invalid config values when change monitor DPI - double screen_scale; - float main_toolbar_height; - - // Zero means it is calculated in init function - ImVec2 minimal_window_size = ImVec2(0, 0); - ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); - ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); - float height_of_volume_type_selector = 0.f; - float input_width = 0.f; - float delete_pos_x = 0.f; - float max_style_name_width = 0.f; - unsigned int icon_width = 0; - - // maximal width and height of style image - Vec2i max_style_image_size = Vec2i(0, 0); - - float indent = 0.f; - float input_offset = 0.f; - float advanced_input_offset = 0.f; - - ImVec2 text_size; - - // maximal size of face name image - Vec2i face_name_size = Vec2i(100, 0); - float face_name_max_width = 100.f; - float face_name_texture_offset_x = 105.f; - - // maximal texture generate jobs running at once - unsigned int max_count_opened_font_files = 10; - - // Only translations needed for calc GUI size - struct Translations - { - std::string font; - std::string size; - std::string depth; - std::string use_surface; - - // advanced - std::string char_gap; - std::string line_gap; - std::string boldness; - std::string italic; - std::string surface_distance; - std::string angle; - std::string collection; - }; - Translations translations; - }; - std::optional m_gui_cfg; + + struct GuiCfg; + std::unique_ptr m_gui_cfg = nullptr; static GuiCfg create_gui_configuration(); + // Is open tree with advanced options bool m_is_advanced_edit_style = false; // when true window will appear near to text volume when open @@ -228,6 +175,7 @@ private: // setted only when wanted to use - not all the time std::optional m_set_window_offset; + // Keep information about stored styles and loaded actual style to compare with Emboss::StyleManager m_style_manager; struct FaceName{ @@ -290,7 +238,8 @@ private: // Text to emboss std::string m_text; - // actual volume + // current selected volume + // NOTE: Be carefull could be uninitialized (removed from Model) ModelVolume *m_volume; // When work with undo redo stack there could be situation that @@ -308,27 +257,6 @@ private: // Value is set only when dragging rotation to calculate actual angle std::optional m_rotate_start_angle; - // Data for drag&drop over surface with mouse - struct SurfaceDrag - { - // hold screen coor offset of cursor from object center - Vec2d mouse_offset; - - // Start dragging text transformations to world - Transform3d world; - - // Invers transformation of text volume instance - // Help convert world transformation to instance space - Transform3d instance_inv; - - // Dragged gl volume - GLVolume *gl_volume; - - // condition for raycaster - RaycastManager::AllowVolumes condition; - - bool exist_hit = true; - }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; @@ -343,27 +271,8 @@ private: // drawing icons IconManager m_icon_manager; - std::vector m_icons; - + IconManager::VIcons m_icons; void init_icons(); - enum class IconType : unsigned { - rename = 0, - erase, - add, - save, - undo, - italic, - unitalic, - bold, - unbold, - system_selector, - open_file, - // automatic calc of icon's count - _count - }; - enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; - const IconManager::Icon& get_icon(IconType type, IconState state); - bool draw_button(IconType icon, bool disable = false); // only temporary solution static const std::string M_ICON_FILENAME; diff --git a/src/slic3r/GUI/IconManager.hpp b/src/slic3r/GUI/IconManager.hpp index 78b1395dc..aa7afda80 100644 --- a/src/slic3r/GUI/IconManager.hpp +++ b/src/slic3r/GUI/IconManager.hpp @@ -63,6 +63,8 @@ public: // && size.x > 0 && size.y > 0 && tl.x != br.x && tl.y != br.y; }; using Icons = std::vector >; + // Vector of icons, each vector contain multiple use of a SVG render + using VIcons = std::vector; /// /// Initialize raster texture on GPU with given images @@ -71,7 +73,7 @@ public: /// Define files and its /// Rasterized icons stored on GPU, /// Same size and order as input, each item of vector is set of texture in order by RasterType - std::vector init(const InitTypes &input); + VIcons init(const InitTypes &input); /// /// Initialize multiple icons with same settings for size and type @@ -83,7 +85,7 @@ public: /// together color, white and gray = RasterType::color | RasterType::white_only_data | RasterType::gray_only_data /// Rasterized icons stored on GPU, /// Same size and order as file_paths, each item of vector is set of texture in order by RasterType - std::vector init(const std::vector &file_paths, const ImVec2 &size, RasterType type = RasterType::color); + VIcons init(const std::vector &file_paths, const ImVec2 &size, RasterType type = RasterType::color); /// /// Release icons which are hold only by this manager diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index fd0a5ec59..3e85d1d88 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -3364,5 +3364,28 @@ void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& v } #endif // ENABLE_WORLD_COORDINATE +ModelVolume *get_selected_volume(const Selection &selection) +{ + const GLVolume *vol_gl = get_selected_gl_volume(selection); + const ModelObjectPtrs &objects = selection.get_model()->objects; + return get_model_volume(*vol_gl, objects); +} + +const GLVolume *get_selected_gl_volume(const Selection &selection) +{ + int object_idx = selection.get_object_idx(); + // is more object selected? + if (object_idx == -1) + return nullptr; + + const auto &list = selection.get_volume_idxs(); + // is more volumes selected? + if (list.size() != 1) + return nullptr; + + unsigned int volume_idx = *list.begin(); + return selection.get_volume(volume_idx); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index c1c97bd2e..a000f661b 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -17,6 +17,7 @@ namespace Slic3r { class Shader; class Model; class ModelObject; +class ModelVolume; class GLVolume; class GLArrow; class GLCurvedArrow; @@ -519,6 +520,9 @@ private: #endif // ENABLE_WORLD_COORDINATE }; +ModelVolume *get_selected_volume(const Selection &selection); +const GLVolume *get_selected_gl_volume(const Selection &selection); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp new file mode 100644 index 000000000..d2986bbf4 --- /dev/null +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -0,0 +1,244 @@ +#include "SurfaceDrag.hpp" + +#include "libslic3r/Model.hpp" // ModelVolume +#include "GLCanvas3D.hpp" +#include "slic3r/Utils/RaycastManager.hpp" + +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + +#include "libslic3r/Emboss.hpp" + +// getter on camera needs +//#include "slic3r/GUI/GUI_App.hpp" +//#include "Plater.hpp" + +namespace Slic3r::GUI { + +static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera) +{ + const Transform3d &volume_tr = volume.get_matrix(); + assert(volume.text_configuration.has_value()); + + auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d { + Transform3d to_world = instrance_tr * volume_tr; + + // Use fix of .3mf loaded tranformation when exist + if (volume.text_configuration->fix_3mf_tr.has_value()) + to_world = to_world * (*volume.text_configuration->fix_3mf_tr); + // zero point of volume in world coordinate system + Vec3d volume_center = to_world.translation(); + // screen coordinate of volume center + Vec2i coor = CameraUtils::project(camera, volume_center); + return coor.cast() - screen_coor; + }; + + auto object = volume.get_object(); + assert(!object->instances.empty()); + // Speed up for one instance + if (object->instances.size() == 1) + return calc_offset(object->instances.front()->get_matrix()); + + Vec2d nearest_offset; + double nearest_offset_size = std::numeric_limits::max(); + for (const ModelInstance *instance : object->instances) { + Vec2d offset = calc_offset(instance->get_matrix()); + double offset_size = offset.norm(); + if (nearest_offset_size < offset_size) + continue; + nearest_offset_size = offset_size; + nearest_offset = offset; + } + return nearest_offset; +} + + // Calculate scale in world +static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) +{ + Vec3d from_dir = from * dir; + Vec3d to_dir = to * dir; + double from_scale_sq = from_dir.squaredNorm(); + double to_scale_sq = to_dir.squaredNorm(); + if (is_approx(from_scale_sq, to_scale_sq, 1e-3)) + return {}; // no scale + return sqrt(from_scale_sq / to_scale_sq); +} + +bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + RaycastManager &raycast_manager) +{ + // Fix when leave window during dragging + // Fix when click right button + if (surface_drag.has_value() && !mouse_event.Dragging()) { + // write transformation from UI into model + canvas.do_move(L("Surface move")); + + // allow moving with object again + canvas.enable_moving(true); + surface_drag.reset(); + + // only left up is correct + // otherwise it is fix state and return false + return mouse_event.LeftUp(); + } + + if (mouse_event.Moving()) + return false; + + // detect start text dragging + if (mouse_event.LeftDown()) { + // selected volume + GLVolume *gl_volume = get_selected_gl_volume(canvas); + if (gl_volume == nullptr) + return false; + + // is selected volume closest hovered? + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + int hovered_idx = canvas.get_first_hover_volume_idx(); + if (hovered_idx < 0 || + hovered_idx >= gl_volumes.size() || + gl_volumes[hovered_idx] != gl_volume) + return false; + + const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + const ModelInstance *instance = get_model_instance(*gl_volume, *object); + const ModelVolume *volume = get_model_volume(*gl_volume, *object); + + assert(object != nullptr && instance != nullptr && volume != nullptr); + if (object == nullptr || instance == nullptr || volume == nullptr) + return false; + + const ModelVolumePtrs &volumes = object->volumes; + std::vector allowed_volumes_id; + if (volumes.size() > 1) { + allowed_volumes_id.reserve(volumes.size() - 1); + for (auto &v : volumes) { + // skip actual selected object + if (v->id() == volume->id()) + continue; + // drag only above part not modifiers or negative surface + if (!v->is_model_part()) + continue; + allowed_volumes_id.emplace_back(v->id().id); + } + } + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + RaycastManager::Meshes meshes = create_meshes(canvas, condition); + // initialize raycasters + // INFO: It could slows down for big objects + // (may be move to thread and do not show drag until it finish) + raycast_manager.actualize(instance, &condition, &meshes); + + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera); + + Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); + + if (volume->text_configuration.has_value()) { + const TextConfiguration &tc = *volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); + } + + Transform3d instance_tr = instance->get_matrix(); + Transform3d instance_tr_inv = instance_tr.inverse(); + Transform3d world_tr = instance_tr * volume_tr; + surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; + + // disable moving with object by mouse + canvas.enable_moving(false); + return true; + } + + // Dragging starts out of window + if (!surface_drag.has_value()) + return false; + + if (mouse_event.Dragging()) { + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset; + + std::optional hit = ray_from_camera( + raycast_manager, offseted_mouse, camera, &surface_drag->condition); + + surface_drag->exist_hit = hit.has_value(); + if (!hit.has_value()) { + // cross hair need redraw + canvas.set_as_dirty(); + return true; + } + + auto world_linear = surface_drag->world.linear(); + // Calculate offset: transformation to wanted position + { + // Reset skew of the text Z axis: + // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. + Vec3d old_z = world_linear.col(2); + Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); + world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + } + + Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() + auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); + Transform3d world_new = z_rotation * surface_drag->world; + auto world_new_linear = world_new.linear(); + + // Fix direction of up vector + { + Vec3d z_world = world_new_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = Emboss::suggest_up(z_world); + + Vec3d y_world = world_new_linear.col(1); + auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + + world_new = y_rotation * world_new; + world_new_linear = world_new.linear(); + } + + // Edit position from right + Transform3d volume_new{Eigen::Translation(surface_drag->instance_inv * hit->position)}; + volume_new.linear() = surface_drag->instance_inv.linear() * world_new_linear; + + // Check that transformation matrix is valid transformation + assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) + return true; + + // Check that scale in world did not changed + assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); + assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); + + const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects); + if (volume != nullptr && volume->text_configuration.has_value()) { + const TextConfiguration &tc = *volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_new = volume_new * (*tc.fix_3mf_tr); + + // apply move in Z direction and rotation by up vector + Emboss::apply_transformation(tc.style.prop, volume_new); + } + + // Update transformation for all instances + for (GLVolume *vol : canvas.get_volumes().volumes) { + if (vol->object_idx() != surface_drag->gl_volume->object_idx() || vol->volume_idx() != surface_drag->gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(volume_new); + } + + canvas.set_as_dirty(); + return true; + } + return false; +} + +} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp new file mode 100644 index 000000000..c3dc9cd2d --- /dev/null +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -0,0 +1,47 @@ +#ifndef slic3r_SurfaceDrag_hpp_ +#define slic3r_SurfaceDrag_hpp_ + +#include +#include "libslic3r/Point.hpp" // Vec2d, Transform3d +#include "slic3r/Utils/RaycastManager.hpp" +#include "wx/event.h" // wxMouseEvent + +namespace Slic3r { +class GLVolume; +} // namespace Slic3r + +namespace Slic3r::GUI { +class GLCanvas3D; +struct Camera; + +// Data for drag&drop over surface with mouse +struct SurfaceDrag +{ + // hold screen coor offset of cursor from object center + Vec2d mouse_offset; + + // Start dragging text transformations to world + Transform3d world; + + // Invers transformation of text volume instance + // Help convert world transformation to instance space + Transform3d instance_inv; + + // Dragged gl volume + GLVolume *gl_volume; + + // condition for raycaster + RaycastManager::AllowVolumes condition; + + // Flag whether coordinate hit some volume + bool exist_hit = true; +}; + +bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + RaycastManager &raycast_manager); + +} // namespace Slic3r::GUI +#endif // slic3r_SurfaceDrag_hpp_ \ No newline at end of file diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 3b0ace2a3..18c9bb2f1 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -295,3 +295,47 @@ RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, con return items.end(); return it; } + +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + +namespace Slic3r::GUI{ + +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition) +{ + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + const std::vector> &casters = *scene_casters; + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; + + RaycastManager::Meshes meshes; + for (const std::shared_ptr &caster : casters) { + int index = SceneRaycaster::decode_id(type, caster->get_id()); + if (index < 0 || index >= gl_volumes.size()) + continue; + const GLVolume *gl_volume = gl_volumes[index]; + const ModelVolume *volume = get_model_volume(*gl_volume, objects); + size_t id = volume->id().id; + if (condition.skip(id)) + continue; + auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); + meshes.emplace_back(std::make_pair(id, std::move(mesh))); + } + return meshes; +} + + +std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + return raycaster.first_hit(point, direction, skip); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 4ef9b7ca7..a3cd3ef91 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -144,6 +144,29 @@ public: Transform3d get_transformation(const TrKey &tr_key) const; }; +class GLCanvas3D; +/// +/// Use scene Raycasters and prepare data for actualize RaycasterManager +/// +/// contain Scene raycasters +/// Limit for scene casters +/// Meshes +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition); + +struct Camera; +/// +/// Unproject on mesh by Mesh raycasters +/// +/// Position of mouse on screen +/// Projection params +/// Define which caster will be skipped, null mean no skip +/// Position on surface, normal direction in world coorinate +/// + key, to know hitted instance and volume +std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip); + } // namespace Slic3r::GUI #endif // slic3r_RaycastManager_hpp_ From 6300396707ba8e75881ebf04d6dcccde52c35344 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 10:39:59 +0100 Subject: [PATCH 041/201] More trace for detect place of crash on Mac --- src/slic3r/Utils/WxFontUtils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index 2851098f2..4156bb998 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -174,8 +174,11 @@ std::string WxFontUtils::store_wxFont(const wxFont &font) wxFont WxFontUtils::load_wxFont(const std::string &font_descriptor) { + BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "'font descriptor string param of load_wxFont()"; wxString font_descriptor_wx(font_descriptor); + BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor_wx.c_str() << "' wx string descriptor"; wxFont wx_font(font_descriptor_wx); + BOOST_LOG_TRIVIAL(trace) << "loaded font is '" << get_human_readable_name(wx_font) << "'."; return wx_font; } From 6b2c834466ba1bf8ad30124924559aec58e502c0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 11:17:32 +0100 Subject: [PATCH 042/201] Fix: ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp:3076:44: warning: braces around scalar initializer [-Wbraced-scalar-init] ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp:3693:74: warning: suggest braces around initialization of subobject [-Wmissing-braces] ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp:461:13: warning: unused function 'calc_mouse_to_center_text_offset' [-Wunused-function] ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp:165:12: note: forward declaration of 'Slic3r::GUI::GLGizmoEmboss::GuiCfg' --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 115 ++---------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 58 +++++++++++- src/slic3r/GUI/SurfaceDrag.cpp | 15 ++-- 3 files changed, 71 insertions(+), 117 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 298829c01..90325e211 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -120,62 +120,6 @@ template void to_range_pi_pi(T& angle) } // namespace priv using namespace priv; -// This configs holds GUI layout size given by translated texts. -// etc. When language changes, GUI is recreated and this class constructed again, -// so the change takes effect. (info by GLGizmoFdmSupports.hpp) -struct GLGizmoEmboss::GuiCfg -{ - // Detect invalid config values when change monitor DPI - double screen_scale; - float main_toolbar_height; - - // Zero means it is calculated in init function - ImVec2 minimal_window_size = ImVec2(0, 0); - ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); - ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); - float height_of_volume_type_selector = 0.f; - float input_width = 0.f; - float delete_pos_x = 0.f; - float max_style_name_width = 0.f; - unsigned int icon_width = 0; - - // maximal width and height of style image - Vec2i max_style_image_size = Vec2i(0, 0); - - float indent = 0.f; - float input_offset = 0.f; - float advanced_input_offset = 0.f; - - ImVec2 text_size; - - // maximal size of face name image - Vec2i face_name_size = Vec2i(100, 0); - float face_name_max_width = 100.f; - float face_name_texture_offset_x = 105.f; - - // maximal texture generate jobs running at once - unsigned int max_count_opened_font_files = 10; - - // Only translations needed for calc GUI size - struct Translations - { - std::string font; - std::string size; - std::string depth; - std::string use_surface; - - // advanced - std::string char_gap; - std::string line_gap; - std::string boldness; - std::string italic; - std::string surface_distance; - std::string angle; - std::string collection; - }; - Translations translations; -}; - GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) : GLGizmoBase(parent, M_ICON_FILENAME, -2) , m_volume(nullptr) @@ -402,14 +346,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) } namespace priv { -/// -/// Calculate offset from mouse position to center of text -/// -/// Screan mouse position -/// Selected volume(text) -/// Offset in screan coordinate -static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVolume &mv); - /// /// Get transformation to world /// - use fix after store to 3mf when exists @@ -458,43 +394,6 @@ Transform3d priv::world_matrix(const Selection &selection) return world_matrix(gl_volume, selection.get_model()); } -Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolume& mv) { - const Transform3d &volume_tr = mv.get_matrix(); - const Camera &camera = wxGetApp().plater()->get_camera(); - assert(mv.text_configuration.has_value()); - - auto calc_offset = [&mouse, &volume_tr, &camera, &mv] - (const Transform3d &instrance_tr) -> Vec2d { - Transform3d to_world = instrance_tr * volume_tr; - - // Use fix of .3mf loaded tranformation when exist - if (mv.text_configuration->fix_3mf_tr.has_value()) - to_world = to_world * (*mv.text_configuration->fix_3mf_tr); - // zero point of volume in world coordinate system - Vec3d volume_center = to_world.translation(); - // screen coordinate of volume center - Vec2i coor = CameraUtils::project(camera, volume_center); - return coor.cast() - mouse; - }; - - auto object = mv.get_object(); - assert(!object->instances.empty()); - // Speed up for one instance - if (object->instances.size() == 1) - return calc_offset(object->instances.front()->get_matrix()); - - Vec2d nearest_offset; - double nearest_offset_size = std::numeric_limits::max(); - for (const ModelInstance *instance : object->instances) { - Vec2d offset = calc_offset(instance->get_matrix()); - double offset_size = offset.norm(); - if (nearest_offset_size < offset_size) continue; - nearest_offset_size = offset_size; - nearest_offset = offset; - } - return nearest_offset; -} - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? @@ -679,7 +578,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); - if (m_gui_cfg == nullptr || // Exist configuration - first run + if (!m_gui_cfg.has_value() || // Exist configuration - first run m_gui_cfg->screen_scale != screen_scale || // change of DPI m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port ) { @@ -687,7 +586,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) GuiCfg cfg = create_gui_configuration(); cfg.screen_scale = screen_scale; cfg.main_toolbar_height = main_toolbar_height; - m_gui_cfg = std::make_unique(std::move(cfg)); + m_gui_cfg.emplace(std::move(cfg)); // set position near toolbar m_set_window_offset = ImVec2(-1.f, -1.f); @@ -816,7 +715,7 @@ void GLGizmoEmboss::on_set_state() // Try(when exist) set text configuration by volume set_volume_by_selection(); - // when open window by "T" and no valid volume is selected, so Create new one + // when open window by "T" and no valid volume is selected, so Create new one if (m_volume == nullptr || get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr ) { // reopen gizmo when new object is created @@ -832,7 +731,7 @@ void GLGizmoEmboss::on_set_state() if (m_allow_open_near_volume) { m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); } else { - if (m_gui_cfg != nullptr) + if (m_gui_cfg.has_value()) priv::change_window_position(m_set_window_offset, false); else m_set_window_offset = ImVec2(-1, -1); @@ -3073,7 +2972,7 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) std::optional priv::calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection) { // Move object on surface - auto cond = RaycastManager::SkipVolume({volume.id().id}); + auto cond = RaycastManager::SkipVolume(volume.id().id); raycast_manager.actualize(volume.get_object(), &cond); //const Selection &selection = m_parent.get_selection(); @@ -3690,8 +3589,8 @@ void priv::start_create_volume_job(const ModelObject *object, bool is_outside = volume_type == ModelVolumeType::MODEL_PART; // check that there is not unexpected volume type assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER); - CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside, - std::move(sources), volume_type, object->id()}; + SurfaceVolumeData sfvd{volume_trmat, is_outside, std::move(sources)}; + CreateSurfaceVolumeData surface_data{std::move(emboss_data), std::move(sfvd), volume_type, object->id()}; job = std::make_unique(std::move(surface_data)); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 5c5a63ebe..ca4d05331 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -162,8 +162,62 @@ private: void create_notification_not_valid_font(const TextConfiguration& tc); void remove_notification_not_valid_font(); - struct GuiCfg; - std::unique_ptr m_gui_cfg = nullptr; + // This configs holds GUI layout size given by translated texts. + // etc. When language changes, GUI is recreated and this class constructed again, + // so the change takes effect. (info by GLGizmoFdmSupports.hpp) + struct GuiCfg + { + // Detect invalid config values when change monitor DPI + double screen_scale; + float main_toolbar_height; + + // Zero means it is calculated in init function + ImVec2 minimal_window_size = ImVec2(0, 0); + ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); + ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); + float height_of_volume_type_selector = 0.f; + float input_width = 0.f; + float delete_pos_x = 0.f; + float max_style_name_width = 0.f; + unsigned int icon_width = 0; + + // maximal width and height of style image + Vec2i max_style_image_size = Vec2i(0, 0); + + float indent = 0.f; + float input_offset = 0.f; + float advanced_input_offset = 0.f; + + ImVec2 text_size; + + // maximal size of face name image + Vec2i face_name_size = Vec2i(100, 0); + float face_name_max_width = 100.f; + float face_name_texture_offset_x = 105.f; + + // maximal texture generate jobs running at once + unsigned int max_count_opened_font_files = 10; + + // Only translations needed for calc GUI size + struct Translations + { + std::string font; + std::string size; + std::string depth; + std::string use_surface; + + // advanced + std::string char_gap; + std::string line_gap; + std::string boldness; + std::string italic; + std::string surface_distance; + std::string angle; + std::string collection; + }; + Translations translations; + }; + std::optional m_gui_cfg; static GuiCfg create_gui_configuration(); // Is open tree with advanced options diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index d2986bbf4..46b612158 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -3,18 +3,19 @@ #include "libslic3r/Model.hpp" // ModelVolume #include "GLCanvas3D.hpp" #include "slic3r/Utils/RaycastManager.hpp" - #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/CameraUtils.hpp" - #include "libslic3r/Emboss.hpp" -// getter on camera needs -//#include "slic3r/GUI/GUI_App.hpp" -//#include "Plater.hpp" - namespace Slic3r::GUI { - + +/// +/// Calculate offset from mouse position to center of text +/// +/// Position on screen[in Px] e.g. mouse position +/// Selected volume(text) +/// Actual position and view direction of camera +/// Offset in screen coordinate static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera) { const Transform3d &volume_tr = volume.get_matrix(); From a1a57eb61c5823eb2757b80a5b6e6aff49138a97 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 15:24:33 +0100 Subject: [PATCH 043/201] Separate drawing of cross hair --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 57 ++++++++++--------------- src/slic3r/GUI/ImGuiWrapper.cpp | 11 +++++ src/slic3r/GUI/ImGuiWrapper.hpp | 14 ++++++ 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 90325e211..7bf9635a4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -137,6 +137,14 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) // Private namespace with helper function for create volume namespace priv { + +/// +/// Check if volume type is possible use for new text volume +/// +/// Type +/// True when allowed otherwise false +static bool is_valid(ModelVolumeType volume_type); + /// /// Prepare data for emboss /// @@ -146,8 +154,6 @@ namespace priv { /// Base data for emboss text static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr> &cancel); -static bool is_valid(ModelVolumeType volume_type); - /// /// Start job for add new volume to object with given transformation /// @@ -200,6 +206,7 @@ static void find_closest_volume(const Selection &selection, /// Screen coordinat, where to create new object laying on bed static void start_create_object_job(DataBase &emboss_data, const Vec2d &coor); +// Loaded icons enum // Have to match order of files in function GLGizmoEmboss::init_icons() enum class IconType : unsigned { rename = 0, @@ -218,21 +225,13 @@ enum class IconType : unsigned { }; // Define rendered version of icon enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; +// selector for icon by enum const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType type, IconState state); +// short call of Slic3r::GUI::button bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); } // namespace priv -bool priv::is_valid(ModelVolumeType volume_type){ - if (volume_type == ModelVolumeType::MODEL_PART || - volume_type == ModelVolumeType::NEGATIVE_VOLUME || - volume_type == ModelVolumeType::PARAMETER_MODIFIER) - return true; - - BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int)volume_type; - return false; -} - void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { if (!priv::is_valid(volume_type)) return; @@ -350,7 +349,7 @@ namespace priv { /// Get transformation to world /// - use fix after store to 3mf when exists /// -/// +/// Scene volume /// To identify MovelVolume with fix transformation /// static Transform3d world_matrix(const GLVolume *gl_volume, const Model *model); @@ -544,26 +543,6 @@ static void draw_mouse_offset(const std::optional &offset) } #endif // SHOW_OFFSET_DURING_DRAGGING -namespace priv { -static void draw_cross_hair(const ImVec2 &position, - float radius = 16.f, - ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)), - int num_segments = 0, - float thickness = 4.f); -} // namespace priv - -void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) -{ - auto draw_list = ImGui::GetOverlayDrawList(); - draw_list->AddCircle(position, 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(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius); - ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius); - draw_list->AddLine(start, end, color, thickness); - } -} - void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { set_volume_by_selection(); @@ -610,7 +589,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) ImVec4(1.f, .3f, .3f, .75f) ); // Warning color const float radius = 16.f; - priv::draw_cross_hair(center, radius, color); + ImGuiWrapper::draw_cross_hair(center, radius, color); } #ifdef SHOW_FINE_POSITION @@ -3519,6 +3498,16 @@ std::string GLGizmoEmboss::get_file_name(const std::string &file_path) // priv namespace implementation /////////////// +bool priv::is_valid(ModelVolumeType volume_type) +{ + if (volume_type == ModelVolumeType::MODEL_PART || volume_type == ModelVolumeType::NEGATIVE_VOLUME || + volume_type == ModelVolumeType::PARAMETER_MODIFIER) + return true; + + BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int) volume_type; + return false; +} + DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr>& cancel) { // create volume_name diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 605757051..55bf57602 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1514,6 +1514,17 @@ void ImGuiWrapper::draw( } } +void ImGuiWrapper::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) { + auto draw_list = ImGui::GetOverlayDrawList(); + draw_list->AddCircle(position, 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(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius); + ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius); + draw_list->AddLine(start, end, color, thickness); + } +} + bool ImGuiWrapper::contain_all_glyphs(const ImFont *font, const std::string &text) { diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 56b5daee6..077bf568d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -199,6 +199,20 @@ public: ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT), float thickness = 3.f); + /// + /// Draw symbol of cross hair + /// + /// Center of cross hair + /// Circle radius + /// Color of symbol + /// Precission of circle + /// Thickness of Line in symbol + static void draw_cross_hair(const ImVec2 &position, + float radius = 16.f, + ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)), + int num_segments = 0, + float thickness = 4.f); + /// /// Check that font ranges contain all chars in string /// (rendered Unicodes are stored in GlyphRanges) From b7f4159d57b1769dfbdf0f91c16afc01b604d02c Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 16:17:09 +0100 Subject: [PATCH 044/201] extend functionality of surface drag --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 104 +----------------------- src/slic3r/GUI/SurfaceDrag.cpp | 99 +++++++++++++++++++++- src/slic3r/GUI/SurfaceDrag.hpp | 38 +++++++++ src/slic3r/Utils/RaycastManager.cpp | 22 ++--- src/slic3r/Utils/RaycastManager.hpp | 4 +- 5 files changed, 149 insertions(+), 118 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 7bf9635a4..a0a367d93 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -345,16 +345,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) } namespace priv { -/// -/// Get transformation to world -/// - use fix after store to 3mf when exists -/// -/// Scene volume -/// To identify MovelVolume with fix transformation -/// -static Transform3d world_matrix(const GLVolume *gl_volume, const Model *model); -static Transform3d world_matrix(const Selection &selection); - /// /// Change position of emboss window /// @@ -363,36 +353,6 @@ static Transform3d world_matrix(const Selection &selection); static void change_window_position(std::optional &output_window_offset, bool try_to_fix); } // namespace priv -Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) -{ - if (!gl_volume) - return Transform3d::Identity(); - Transform3d res = gl_volume->world_matrix(); - - if (!model) - return res; - - const ModelVolume* mv = get_model_volume(*gl_volume, model->objects); - if (!mv) - return res; - - const std::optional &tc = mv->text_configuration; - if (!tc.has_value()) - return res; - - const std::optional &fix = tc->fix_3mf_tr; - if (!fix.has_value()) - return res; - - return res * fix->inverse(); -} - -Transform3d priv::world_matrix(const Selection &selection) -{ - const GLVolume *gl_volume = get_selected_gl_volume(selection); - return world_matrix(gl_volume, selection.get_model()); -} - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? @@ -1115,17 +1075,6 @@ static inline void execute_job(std::shared_ptr j) }); } -namespace priv { -/// -/// Calculate translation of text volume onto surface of model -/// -/// Text -/// AABB trees of object. Actualize object containing text -/// Transformation of actual instance -/// Offset of volume in volume coordinate -std::optional calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection); -} // namespace priv - bool GLGizmoEmboss::process() { // no volume is selected -> selection from right panel @@ -1160,7 +1109,7 @@ bool GLGizmoEmboss::process() // when it is new applying of use surface than move origin onto surfaca if (!m_volume->text_configuration->style.prop.use_surface) { - auto offset = priv::calc_surface_offset(*m_volume, m_raycast_manager, m_parent.get_selection()); + auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager); if (offset.has_value()) text_tr *= Eigen::Translation(*offset); } @@ -1231,7 +1180,7 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { if (sel.is_empty()) return false; // camera direction transformed into volume coordinate system - Transform3d to_world = priv::world_matrix(sel); + Transform3d to_world = world_matrix_fixed(sel); Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; cam_dir_tr.normalize(); @@ -2949,53 +2898,6 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) m_parent.do_rotate(snapshot_name); } -std::optional priv::calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection) { - // Move object on surface - auto cond = RaycastManager::SkipVolume(volume.id().id); - raycast_manager.actualize(volume.get_object(), &cond); - - //const Selection &selection = m_parent.get_selection(); - const GLVolume *gl_volume = get_selected_gl_volume(selection); - Transform3d to_world = priv::world_matrix(gl_volume, selection.get_model()); - Vec3d point = to_world * Vec3d::Zero(); - Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); - - // ray in direction of text projection(from volume zero to z-dir) - std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); - - // Try to find closest point when no hit object in emboss direction - if (!hit_opt.has_value()) { - std::optional close_point_opt = raycast_manager.closest(point); - - // It should NOT appear. Closest point always exists. - assert(close_point_opt.has_value()); - if (!close_point_opt.has_value()) - return {}; - - // It is no neccesary to move with origin by very small value - if (close_point_opt->squared_distance < EPSILON) - return {}; - - const RaycastManager::ClosePoint &close_point = *close_point_opt; - Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); - Vec3d hit_world = hit_tr * close_point.point; - Vec3d offset_world = hit_world - point; // vector in world - Vec3d offset_volume = to_world.inverse().linear() * offset_world; - return offset_volume; - } - - // It is no neccesary to move with origin by very small value - const RaycastManager::Hit &hit = *hit_opt; - if (hit.squared_distance < EPSILON) - return {}; - Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); - Vec3d hit_world = hit_tr * hit.position; - Vec3d offset_world = hit_world - point; // vector in world - // TIP: It should be close to only z move - Vec3d offset_volume = to_world.inverse().linear() * offset_world; - return offset_volume; -} - void GLGizmoEmboss::draw_advanced() { const auto &ff = m_style_manager.get_font_file_with_cache(); @@ -3610,7 +3512,7 @@ bool priv::start_create_volume_on_surface_job( auto cond = RaycastManager::AllowVolumes({vol_id}); RaycastManager::Meshes meshes = create_meshes(canvas, cond); - raycaster.actualize(obj, &cond, &meshes); + raycaster.actualize(*obj, &cond, &meshes); const Camera &camera = plater->get_camera(); std::optional hit = ray_from_camera(raycaster, screen_coor, camera, &cond); diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 46b612158..33f64c0c6 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -104,11 +104,14 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, gl_volumes[hovered_idx] != gl_volume) return false; - const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + assert(object != nullptr); + if (object == nullptr) + return false; + const ModelInstance *instance = get_model_instance(*gl_volume, *object); const ModelVolume *volume = get_model_volume(*gl_volume, *object); - - assert(object != nullptr && instance != nullptr && volume != nullptr); + assert(instance != nullptr && volume != nullptr); if (object == nullptr || instance == nullptr || volume == nullptr) return false; @@ -131,7 +134,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - raycast_manager.actualize(instance, &condition, &meshes); + raycast_manager.actualize(*instance, &condition, &meshes); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); @@ -242,4 +245,92 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, return false; } +std::optional calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager) { + const GLVolume *gl_volume_ptr = get_selected_gl_volume(selection); + if (gl_volume_ptr == nullptr) + return {}; + const GLVolume& gl_volume = *gl_volume_ptr; + + const ModelObjectPtrs &objects = selection.get_model()->objects; + const ModelVolume* volume = get_model_volume(gl_volume, objects); + if (volume == nullptr) + return {}; + + const ModelInstance* instance = get_model_instance(gl_volume, objects); + if (instance == nullptr) + return {}; + + // Move object on surface + auto cond = RaycastManager::SkipVolume(volume->id().id); + raycast_manager.actualize(*instance, &cond); + + Transform3d to_world = world_matrix_fixed(gl_volume, selection.get_model()->objects); + Vec3d point = to_world * Vec3d::Zero(); + Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); + + // ray in direction of text projection(from volume zero to z-dir) + std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); + + // Try to find closest point when no hit object in emboss direction + if (!hit_opt.has_value()) { + std::optional close_point_opt = raycast_manager.closest(point); + + // It should NOT appear. Closest point always exists. + assert(close_point_opt.has_value()); + if (!close_point_opt.has_value()) + return {}; + + // It is no neccesary to move with origin by very small value + if (close_point_opt->squared_distance < EPSILON) + return {}; + + const RaycastManager::ClosePoint &close_point = *close_point_opt; + Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); + Vec3d hit_world = hit_tr * close_point.point; + Vec3d offset_world = hit_world - point; // vector in world + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; + } + + // It is no neccesary to move with origin by very small value + const RaycastManager::Hit &hit = *hit_opt; + if (hit.squared_distance < EPSILON) + return {}; + Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); + Vec3d hit_world = hit_tr * hit.position; + Vec3d offset_world = hit_world - point; // vector in world + // TIP: It should be close to only z move + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; +} + +Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects) +{ + Transform3d res = gl_volume.world_matrix(); + + const ModelVolume *mv = get_model_volume(gl_volume, objects); + if (!mv) + return res; + + const std::optional &tc = mv->text_configuration; + if (!tc.has_value()) + return res; + + const std::optional &fix = tc->fix_3mf_tr; + if (!fix.has_value()) + return res; + + return res * fix->inverse(); +} + +Transform3d world_matrix_fixed(const Selection &selection) +{ + const GLVolume *gl_volume = get_selected_gl_volume(selection); + assert(gl_volume != nullptr); + if (gl_volume == nullptr) + return Transform3d::Identity(); + + return world_matrix_fixed(*gl_volume, selection.get_model()->objects); +} + } // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp index c3dc9cd2d..a3765f86b 100644 --- a/src/slic3r/GUI/SurfaceDrag.hpp +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -12,6 +12,7 @@ class GLVolume; namespace Slic3r::GUI { class GLCanvas3D; +class Selection; struct Camera; // Data for drag&drop over surface with mouse @@ -37,11 +38,48 @@ struct SurfaceDrag bool exist_hit = true; }; +/// +/// Mouse event handler, when move(drag&drop) volume over model surface +/// NOTE: Dragged volume has to be selected. And also has to be hovered on start of dragging. +/// +/// Contain type of event and mouse position +/// Actual viewport of camera +/// Structure which keep information about dragging +/// Contain gl_volumes and selection +/// AABB trees for raycast in object +/// Refresh state inside of function +/// True when event is processed otherwise false bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, const Camera &camera, std::optional &surface_drag, GLCanvas3D &canvas, RaycastManager &raycast_manager); +/// +/// Calculate translation of volume onto surface of model +/// +/// Must contain only one selected volume, Transformation of current instance +/// AABB trees of object. Actualize object +/// Offset of volume in volume coordinate +std::optional calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager); + +/// +/// Get transformation to world +/// - use fix after store to 3mf when exists +/// +/// Scene volume +/// To identify Model volume with fix transformation +/// Fixed Transformation of gl_volume +Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs& objects); + +/// +/// Get transformation to world +/// - use fix after store to 3mf when exists +/// NOTE: when not one volume selected return identity +/// +/// Selected volume +/// Fixed Transformation of selected volume in selection +Transform3d world_matrix_fixed(const Selection &selection); + } // namespace Slic3r::GUI #endif // slic3r_SurfaceDrag_hpp_ \ No newline at end of file diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 18c9bb2f1..63cb580db 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -7,8 +7,8 @@ namespace priv { using namespace Slic3r; static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); -static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ - return std::make_pair(instance->id().id, volume->id().id); } +static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ + return std::make_pair(instance.id().id, volume.id().id); } static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } @@ -16,23 +16,23 @@ static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrI return is_lower_key(i1.first, i2.first); }; } -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Meshes *meshes) +void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object->volumes, skip, meshes); + priv::actualize(m_meshes, object.volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); bool need_sort = false; // actualize transformation matrices - for (const ModelVolume *volume : object->volumes) { + for (const ModelVolume *volume : object.volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); - for (const ModelInstance *instance : object->instances) { + for (const ModelInstance *instance : object.instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(volume, instance); + TrKey key = priv::create_key(*volume, *instance); auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time @@ -56,9 +56,9 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Mes std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, Meshes *meshes) +void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes) { - const ModelVolumePtrs &volumes = instance->get_object()->volumes; + const ModelVolumePtrs &volumes = instance.get_object()->volumes; // actualize MeshRaycaster priv::actualize(m_meshes, volumes, skip, meshes); @@ -72,9 +72,9 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); - const Transform3d &instrance_tr = instance->get_matrix(); + const Transform3d &instrance_tr = instance.get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(volume, instance); + TrKey key = priv::create_key(*volume, instance); auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index a3cd3ef91..41ec82d6c 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -86,8 +86,8 @@ public: /// Model representation /// Condifiton for skip actualization /// Speed up for already created AABBtrees - void actualize(const ModelObject *object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); - void actualize(const ModelInstance *instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); + void actualize(const ModelObject &object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); + void actualize(const ModelInstance &instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); class SkipVolume: public ISkip { From 4f2cf00323657d352bc1317cd67018aeac1d9a0c Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 16:35:00 +0100 Subject: [PATCH 045/201] Hide execute job --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 176 ++++++++++++------------ 1 file changed, 91 insertions(+), 85 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index a0a367d93..a323f06d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -56,6 +56,7 @@ #define SHOW_WX_WEIGHT_INPUT #define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position #define ALLOW_OPEN_NEAR_VOLUME +#define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread #endif // ALLOW_DEBUG_MODE using namespace Slic3r; @@ -228,7 +229,15 @@ enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ } // selector for icon by enum const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType type, IconState state); // short call of Slic3r::GUI::button -bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); +static bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); + +/// +/// Apply camera direction for emboss direction +/// +/// Define view vector +/// Containe Selected Model to modify +/// True when apply change otherwise false +static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas); } // namespace priv @@ -344,15 +353,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) return used; } -namespace priv { -/// -/// Change position of emboss window -/// -/// -/// When True Only move to be full visible otherwise reset position -static void change_window_position(std::optional &output_window_offset, bool try_to_fix); -} // namespace priv - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? @@ -604,22 +604,14 @@ namespace priv { /// Move window for edit emboss text near to embossed object /// NOTE: embossed object must be selected /// -ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size) -{ - const Selection::IndicesList indices = selection.get_volume_idxs(); - // no selected volume - if (indices.empty()) return {}; - const GLVolume *volume = selection.get_volume(*indices.begin()); - // bad volume selected (e.g. deleted one) - if (volume == nullptr) return {}; +static ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size); - const Camera &camera = wxGetApp().plater()->get_camera(); - Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *volume); - - ImVec2 c_size(canvas_size.get_width(), canvas_size.get_height()); - ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, c_size); - return offset; -} +/// +/// Change position of emboss window +/// +/// +/// When True Only move to be full visible otherwise reset position +static void change_window_position(std::optional &output_window_offset, bool try_to_fix); } // namespace priv void GLGizmoEmboss::on_set_state() @@ -1056,6 +1048,8 @@ void GLGizmoEmboss::calculate_scale() { m_style_manager.clear_imgui_font(); } +#ifdef EXECUTE_PROCESS_ON_MAIN_THREAD +namespace priv { // Run Job on main thread (blocking) - ONLY DEBUG static inline void execute_job(std::shared_ptr j) { @@ -1074,6 +1068,8 @@ static inline void execute_job(std::shared_ptr j) j->finalize(false, e_ptr); }); } +} // namespace priv +#endif bool GLGizmoEmboss::process() { @@ -1124,12 +1120,13 @@ bool GLGizmoEmboss::process() job = std::make_unique(std::move(data)); } - //* +#ifndef EXECUTE_PROCESS_ON_MAIN_THREAD auto &worker = wxGetApp().plater()->get_ui_job_worker(); queue_job(worker, std::move(job)); - /*/ // Run Job on main thread (blocking) - ONLY DEBUG - execute_job(std::move(job)); - // */ +#else + // Run Job on main thread (blocking) - ONLY DEBUG + priv::execute_job(std::move(job)); +#endif // EXECUTE_PROCESS_ON_MAIN_THREAD // notification is removed befor object is changed by job remove_notification_not_valid_font(); @@ -1162,62 +1159,6 @@ void GLGizmoEmboss::close() mng.open_gizmo(GLGizmosManager::Emboss); } -namespace priv { - -/// -/// Apply camera direction for emboss direction -/// -/// Define view vector -/// Containe Selected Model to modify -/// True when apply change otherwise false -static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas); -} - -bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { - const Vec3d &cam_dir = camera.get_dir_forward(); - - Selection &sel = canvas.get_selection(); - if (sel.is_empty()) return false; - - // camera direction transformed into volume coordinate system - Transform3d to_world = world_matrix_fixed(sel); - Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; - cam_dir_tr.normalize(); - - Vec3d emboss_dir(0., 0., -1.); - - // check wether cam_dir is already used - if (is_approx(cam_dir_tr, emboss_dir)) return false; - - assert(sel.get_volume_idxs().size() == 1); - GLVolume *gl_volume = sel.get_volume(*sel.get_volume_idxs().begin()); - - Transform3d vol_rot; - Transform3d vol_tr = gl_volume->get_volume_transformation().get_matrix(); - // check whether cam_dir is opposit to emboss dir - if (is_approx(cam_dir_tr, -emboss_dir)) { - // rotate 180 DEG by y - vol_rot = Eigen::AngleAxis(M_PI_2, Vec3d(0., 1., 0.)); - } else { - // calc params for rotation - Vec3d axe = emboss_dir.cross(cam_dir_tr); - axe.normalize(); - double angle = std::acos(emboss_dir.dot(cam_dir_tr)); - vol_rot = Eigen::AngleAxis(angle, axe); - } - - Vec3d offset = vol_tr * Vec3d::Zero(); - Vec3d offset_inv = vol_rot.inverse() * offset; - Transform3d res = vol_tr * - Eigen::Translation(-offset) * - vol_rot * - Eigen::Translation(offset_inv); - //Transform3d res = vol_tr * vol_rot; - gl_volume->set_volume_transformation(Geometry::Transformation(res)); - get_model_volume(*gl_volume, sel.get_model()->objects)->set_transformation(res); - return true; -} - void GLGizmoEmboss::draw_window() { #ifdef ALLOW_DEBUG_MODE @@ -3570,6 +3511,25 @@ void priv::find_closest_volume(const Selection &selection, } } +ImVec2 priv::calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size) +{ + const Selection::IndicesList indices = selection.get_volume_idxs(); + // no selected volume + if (indices.empty()) + return {}; + const GLVolume *volume = selection.get_volume(*indices.begin()); + // bad volume selected (e.g. deleted one) + if (volume == nullptr) + return {}; + + const Camera &camera = wxGetApp().plater()->get_camera(); + Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *volume); + + ImVec2 c_size(canvas_size.get_width(), canvas_size.get_height()); + ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, c_size); + return offset; +} + // Need internals to get window #include "imgui/imgui_internal.h" void priv::change_window_position(std::optional& output_window_offset, bool try_to_fix) { @@ -3606,5 +3566,51 @@ void priv::change_window_position(std::optional& output_window_offset, b output_window_offset = ImVec2(-1, -1); // Cannot } + +bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { + const Vec3d &cam_dir = camera.get_dir_forward(); + + Selection &sel = canvas.get_selection(); + if (sel.is_empty()) return false; + + // camera direction transformed into volume coordinate system + Transform3d to_world = world_matrix_fixed(sel); + Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; + cam_dir_tr.normalize(); + + Vec3d emboss_dir(0., 0., -1.); + + // check wether cam_dir is already used + if (is_approx(cam_dir_tr, emboss_dir)) return false; + + assert(sel.get_volume_idxs().size() == 1); + GLVolume *gl_volume = sel.get_volume(*sel.get_volume_idxs().begin()); + + Transform3d vol_rot; + Transform3d vol_tr = gl_volume->get_volume_transformation().get_matrix(); + // check whether cam_dir is opposit to emboss dir + if (is_approx(cam_dir_tr, -emboss_dir)) { + // rotate 180 DEG by y + vol_rot = Eigen::AngleAxis(M_PI_2, Vec3d(0., 1., 0.)); + } else { + // calc params for rotation + Vec3d axe = emboss_dir.cross(cam_dir_tr); + axe.normalize(); + double angle = std::acos(emboss_dir.dot(cam_dir_tr)); + vol_rot = Eigen::AngleAxis(angle, axe); + } + + Vec3d offset = vol_tr * Vec3d::Zero(); + Vec3d offset_inv = vol_rot.inverse() * offset; + Transform3d res = vol_tr * + Eigen::Translation(-offset) * + vol_rot * + Eigen::Translation(offset_inv); + //Transform3d res = vol_tr * vol_rot; + gl_volume->set_volume_transformation(Geometry::Transformation(res)); + get_model_volume(*gl_volume, sel.get_model()->objects)->set_transformation(res); + return true; +} + // any existing icon filename to not influence GUI const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg"; From ef6ed9708e6ac5ec5612fe44aac070a00ccb63da Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 16:38:28 +0100 Subject: [PATCH 046/201] hide choose SVG file --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 3 +++ src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index a323f06d8..9bcff93f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3198,6 +3198,8 @@ bool GLGizmoEmboss::choose_true_type_file() } #endif // ALLOW_ADD_FONT_BY_FILE + +#ifdef ALLOW_DEBUG_MODE bool GLGizmoEmboss::choose_svg_file() { wxArrayString input_files; @@ -3231,6 +3233,7 @@ bool GLGizmoEmboss::choose_svg_file() // svg.draw(polys); //return add_volume(name, its); } +#endif // ALLOW_DEBUG_MODE void GLGizmoEmboss::create_notification_not_valid_font( const TextConfiguration &tc) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index ca4d05331..26c78f8c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -153,10 +153,6 @@ private: bool on_mouse_for_rotation(const wxMouseEvent &mouse_event); bool on_mouse_for_translate(const wxMouseEvent &mouse_event); - bool choose_font_by_wxdialog(); - bool choose_true_type_file(); - bool choose_svg_file(); - // When open text loaded from .3mf it could be written with unknown font bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); From a8c580c957e7b0037c1e0f22f24e83a0d6095978 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 17:01:52 +0100 Subject: [PATCH 047/201] Move function is_font_changed into EmbossStyleManager --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 71 ++++++------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 3 -- src/slic3r/Utils/EmbossStyleManager.cpp | 39 ++++++++++++++ src/slic3r/Utils/EmbossStyleManager.hpp | 8 ++- 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 9bcff93f4..56c973719 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2558,46 +2558,6 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -bool is_font_changed( - const wxFont &wx_font, const wxFont &wx_font_stored, - const FontProp &prop, const FontProp &prop_stored) -{ - // Exist change in face name? - if(wx_font_stored.GetFaceName() != wx_font.GetFaceName()) return true; - - const std::optional &skew = prop.skew; - bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); - const std::optional &skew_stored = prop_stored.skew; - bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(wx_font_stored); - // is italic changed - if (is_italic != is_stored_italic) - return true; - - const std::optional &boldness = prop.boldness; - bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); - const std::optional &boldness_stored = prop_stored.boldness; - bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(wx_font_stored); - // is bold changed - return is_bold != is_stored_bold; -} - -bool is_font_changed(const StyleManager &mng) { - const std::optional &wx_font_opt = mng.get_wx_font(); - if (!wx_font_opt.has_value()) - return false; - if (!mng.exist_stored_style()) - return false; - const EmbossStyle *stored_style = mng.get_stored_style(); - if (stored_style == nullptr) - return false; - - const std::optional &wx_font_stored_opt = mng.get_stored_wx_font(); - if (!wx_font_stored_opt.has_value()) - return false; - - return is_font_changed(*wx_font_opt, *wx_font_stored_opt, mng.get_style().prop, stored_style->prop); -} - void GLGizmoEmboss::draw_style_edit() { const std::optional &wx_font_opt = m_style_manager.get_wx_font(); assert(wx_font_opt.has_value()); @@ -2606,7 +2566,7 @@ void GLGizmoEmboss::draw_style_edit() { return; } bool exist_stored_style = m_style_manager.exist_stored_style(); - bool exist_change_in_font = is_font_changed(m_style_manager); + bool exist_change_in_font = m_style_manager.is_font_changed(); const GuiCfg::Translations &tr = m_gui_cfg->translations; if (exist_change_in_font || !exist_stored_style) ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font); @@ -2714,7 +2674,6 @@ void GLGizmoEmboss::draw_height(bool use_inch) process(); } - bool GLGizmoEmboss::set_depth() { float &value = m_style_manager.get_style().prop.emboss; @@ -2739,7 +2698,6 @@ void GLGizmoEmboss::draw_depth(bool use_inch) process(); } - bool GLGizmoEmboss::rev_slider(const std::string &name, std::optional& value, const std::optional *default_value, @@ -3170,6 +3128,19 @@ bool GLGizmoEmboss::choose_font_by_wxdialog() } #endif // ALLOW_ADD_FONT_BY_OS_SELECTOR +#if defined ALLOW_ADD_FONT_BY_FILE or defined ALLOW_DEBUG_MODE +namespace priv { +static std::string get_file_name(const std::string &file_path) +{ + size_t pos_last_delimiter = file_path.find_last_of("/\\"); + size_t pos_point = file_path.find_last_of('.'); + size_t offset = pos_last_delimiter + 1; + size_t count = pos_point - pos_last_delimiter - 1; + return file_path.substr(offset, count); +} +} // namespace priv +#endif // ALLOW_ADD_FONT_BY_FILE || ALLOW_DEBUG_MODE + #ifdef ALLOW_ADD_FONT_BY_FILE bool GLGizmoEmboss::choose_true_type_file() { @@ -3185,7 +3156,7 @@ bool GLGizmoEmboss::choose_true_type_file() // use first valid font for (auto &input_file : input_files) { std::string path = std::string(input_file.c_str()); - std::string name = get_file_name(path); + std::string name = priv::get_file_name(path); //make_unique_name(name, m_font_list); const FontProp& prop = m_style_manager.get_font_prop(); EmbossStyle style{ name, path, EmbossStyle::Type::file_path, prop }; @@ -3198,7 +3169,6 @@ bool GLGizmoEmboss::choose_true_type_file() } #endif // ALLOW_ADD_FONT_BY_FILE - #ifdef ALLOW_DEBUG_MODE bool GLGizmoEmboss::choose_svg_file() { @@ -3213,7 +3183,7 @@ bool GLGizmoEmboss::choose_svg_file() if (input_files.size() != 1) return false; auto & input_file = input_files.front(); std::string path = std::string(input_file.c_str()); - std::string name = get_file_name(path); + std::string name = priv::get_file_name(path); NSVGimage *image = nsvgParseFromFile(path.c_str(), "mm", 96.0f); ExPolygons polys = NSVGUtils::to_ExPolygons(image); @@ -3331,15 +3301,6 @@ bool GLGizmoEmboss::is_text_object(const ModelVolume *text) { return true; } -std::string GLGizmoEmboss::get_file_name(const std::string &file_path) -{ - size_t pos_last_delimiter = file_path.find_last_of("/\\"); - size_t pos_point = file_path.find_last_of('.'); - size_t offset = pos_last_delimiter + 1; - size_t count = pos_point - pos_last_delimiter - 1; - return file_path.substr(offset, count); -} - ///////////// // priv namespace implementation /////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 26c78f8c3..5ee2d396a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -335,9 +335,6 @@ public: /// Model volume of Text /// True when object otherwise False static bool is_text_object(const ModelVolume *text); - - // TODO: move to file utils - static std::string get_file_name(const std::string &file_path); }; } // namespace Slic3r::GUI diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index af1aae669..0640dc186 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -228,6 +228,45 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font) return true; } +bool StyleManager::is_font_changed() const +{ + const std::optional &wx_font_opt = get_wx_font(); + if (!wx_font_opt.has_value()) + return false; + if (!exist_stored_style()) + return false; + const EmbossStyle *stored_style = get_stored_style(); + if (stored_style == nullptr) + return false; + + const std::optional &wx_font_stored_opt = get_stored_wx_font(); + if (!wx_font_stored_opt.has_value()) + return false; + + const wxFont &wx_font = *wx_font_opt; + const wxFont &wx_font_stored = *wx_font_stored_opt; + const FontProp &prop = get_style().prop; + const FontProp &prop_stored = stored_style->prop; + + // Exist change in face name? + if(wx_font_stored.GetFaceName() != wx_font.GetFaceName()) return true; + + const std::optional &skew = prop.skew; + bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); + const std::optional &skew_stored = prop_stored.skew; + bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(wx_font_stored); + // is italic changed + if (is_italic != is_stored_italic) + return true; + + const std::optional &boldness = prop.boldness; + bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); + const std::optional &boldness_stored = prop_stored.boldness; + bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(wx_font_stored); + // is bold changed + return is_bold != is_stored_bold; +} + bool StyleManager::is_active_font() { return m_style_cache.font_file.has_value(); } const EmbossStyle* StyleManager::get_stored_style() const diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 08fa72642..1fd920be3 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -124,7 +124,13 @@ public: // True when activ style has same name as some of stored style bool exist_stored_style() const { return m_style_cache.style_index != std::numeric_limits::max(); } - + + /// + /// check whether current style differ to selected + /// + /// + bool is_font_changed() const; + /// /// Setter on wx_font when changed /// From bc437661dcadb48d1170889304a1ceae799a7d26 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Mar 2023 12:52:06 +0100 Subject: [PATCH 048/201] Add log for creation of font descriptor --- src/slic3r/Utils/WxFontUtils.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index 4156bb998..f209a1f7c 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -169,6 +169,13 @@ std::string WxFontUtils::store_wxFont(const wxFont &font) { // wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName(); wxString font_descriptor = font.GetNativeFontInfoDesc(); + BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "' wx string get from GetNativeFontInfoDesc. wxFont " << + "IsOk(" << font.IsOk() << "), " << + "isNull(" << font.IsNull() << ")" << + "IsFree(" << font.IsFree() << "), " << + "IsFixedWidth(" << font.IsFixedWidth() << "), " << + "IsUsingSizeInPixels(" << font.IsUsingSizeInPixels() << "), " << + "Encoding(" << (int)font.GetEncoding() << "), " ; return std::string(font_descriptor.c_str()); } From 4c3ac0d9774e7108f43afbf16d10681f6a068fd3 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Mar 2023 15:05:38 +0100 Subject: [PATCH 049/201] Disable setting font pixel size into wxFont Do not keep wxFont inside of optional --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 63 ++++++++++++++----------- src/slic3r/Utils/EmbossStyleManager.cpp | 12 ++--- src/slic3r/Utils/EmbossStyleManager.hpp | 8 ++-- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 56c973719..9a85eacb3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -59,6 +59,8 @@ #define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread #endif // ALLOW_DEBUG_MODE +//#define USE_PIXEL_SIZE_IN_WX_FONT + using namespace Slic3r; using namespace Slic3r::Emboss; using namespace Slic3r::GUI; @@ -1694,11 +1696,13 @@ bool GLGizmoEmboss::select_facename(const wxString &facename) if (!wxFontEnumerator::IsValidFacename(facename)) return false; // Select font const wxFontEncoding &encoding = m_face_names.encoding; - wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding)); + wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding)); if (!wx_font.IsOk()) return false; +#ifdef USE_PIXEL_SIZE_IN_WX_FONT // wx font could change source file by size of font int point_size = static_cast(m_style_manager.get_font_prop().size_in_mm); wx_font.SetPointSize(point_size); +#endif // USE_PIXEL_SIZE_IN_WX_FONT if (!m_style_manager.set_wx_font(wx_font)) return false; process(); return true; @@ -1709,9 +1713,9 @@ void GLGizmoEmboss::draw_font_list() // Set partial wxString actual_face_name; if (m_style_manager.is_active_font()) { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - if (wx_font_opt.has_value()) - actual_face_name = wx_font_opt->GetFaceName(); + const wxFont &wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) + actual_face_name = wx_font.GetFaceName(); } // name of actual selected font const char * selected = (!actual_face_name.empty()) ? @@ -2347,13 +2351,12 @@ void GLGizmoEmboss::draw_style_list() { bool GLGizmoEmboss::draw_italic_button() { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); + const wxFont &wx_font = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); - if (!wx_font_opt.has_value() || !ff.has_value()) { + if (!wx_font.IsOk() || !ff.has_value()) { draw(*m_icons[(int) IconType::italic][(int)IconState::disabled]); return false; } - const wxFont& wx_font = *wx_font_opt; std::optional &skew = m_style_manager.get_font_prop().skew; bool is_font_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); @@ -2394,13 +2397,12 @@ bool GLGizmoEmboss::draw_italic_button() } bool GLGizmoEmboss::draw_bold_button() { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); + const wxFont &wx_font = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); - if (!wx_font_opt.has_value() || !ff.has_value()) { + if (!wx_font.IsOk() || !ff.has_value()) { draw(get_icon(m_icons, IconType::bold, IconState::disabled)); return false; } - const wxFont &wx_font = *wx_font_opt; std::optional &boldness = m_style_manager.get_font_prop().boldness; bool is_font_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); @@ -2558,13 +2560,18 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -void GLGizmoEmboss::draw_style_edit() { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - assert(wx_font_opt.has_value()); - if (!wx_font_opt.has_value()) { - ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); - return; +void GLGizmoEmboss::draw_style_edit() +{ + { + // Check correct WxFont + const wxFont &wx_font = m_style_manager.get_wx_font(); + assert(wx_font.IsOk()); + if (!wx_font.IsOk()) { + ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); + return; + } } + bool exist_stored_style = m_style_manager.exist_stored_style(); bool exist_change_in_font = m_style_manager.is_font_changed(); const GuiCfg::Translations &tr = m_gui_cfg->translations; @@ -2651,13 +2658,15 @@ bool GLGizmoEmboss::set_height() { if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) return false; +#ifdef USE_PIXEL_SIZE_IN_WX_FONT // store font size into path serialization - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - if (wx_font_opt.has_value()) { - wxFont wx_font = *wx_font_opt; - wx_font.SetPointSize(static_cast(value)); - m_style_manager.set_wx_font(wx_font); + const wxFont &wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) { + wxFont wx_font_new = wx_font; // copy + wx_font_new.SetPointSize(static_cast(value)); + m_style_manager.set_wx_font(wx_font_new); } +#endif return true; } @@ -3226,9 +3235,9 @@ void GLGizmoEmboss::create_notification_not_valid_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(); + const wxFont& wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) { + wxString wx_face_name = wx_font.GetFaceName(); face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8()); } } @@ -3331,10 +3340,8 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st const EmbossStyle &es = style_manager.get_style(); // actualize font path - during changes in gui it could be corrupted // volume must store valid path - assert(style_manager.get_wx_font().has_value()); - assert(style_manager.get_wx_font()->IsOk()); - assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0); - // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); + assert(style_manager.get_wx_font().IsOk()); + assert(es.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0); TextConfiguration tc{es, text}; // Cancel previous Job, when it is in process diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index 0640dc186..100a532b8 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -230,8 +230,8 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font) bool StyleManager::is_font_changed() const { - const std::optional &wx_font_opt = get_wx_font(); - if (!wx_font_opt.has_value()) + const wxFont &wx_font = get_wx_font(); + if (!wx_font.IsOk()) return false; if (!exist_stored_style()) return false; @@ -239,12 +239,10 @@ bool StyleManager::is_font_changed() const if (stored_style == nullptr) return false; - const std::optional &wx_font_stored_opt = get_stored_wx_font(); - if (!wx_font_stored_opt.has_value()) + const wxFont &wx_font_stored = get_stored_wx_font(); + if (!wx_font_stored.IsOk()) return false; - const wxFont &wx_font = *wx_font_opt; - const wxFont &wx_font_stored = *wx_font_stored_opt; const FontProp &prop = get_style().prop; const FontProp &prop_stored = stored_style->prop; @@ -533,7 +531,7 @@ bool StyleManager::set_wx_font(const wxFont &wx_font) { bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr font_file) { if (font_file == nullptr) return false; - m_style_cache.wx_font = wx_font; // copy + m_style_cache.wx_font = wx_font; // copy m_style_cache.font_file = FontFileWithCache(std::move(font_file)); diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 1fd920be3..dd6b9ca12 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -116,8 +116,8 @@ public: const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; } const FontProp &get_font_prop() const { return get_style().prop; } FontProp &get_font_prop() { return get_style().prop; } - const std::optional &get_wx_font() const { return m_style_cache.wx_font; } - const std::optional &get_stored_wx_font() const { return m_style_cache.stored_wx_font; } + const wxFont &get_wx_font() const { return m_style_cache.wx_font; } + const wxFont &get_stored_wx_font() const { return m_style_cache.stored_wx_font; } Slic3r::Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; } bool has_collections() const { return m_style_cache.font_file.font_file != nullptr && m_style_cache.font_file.font_file->infos.size() > 1; } @@ -227,7 +227,7 @@ private: ImFontAtlas atlas = {}; // wx widget font - std::optional wx_font = {}; + wxFont wx_font = {}; // cache for view font name with maximal width in imgui std::string truncated_name; @@ -236,7 +236,7 @@ private: EmbossStyle style = {}; // cache for stored wx font to not create every frame - std::optional stored_wx_font; + wxFont stored_wx_font = {}; // index into m_style_items size_t style_index = std::numeric_limits::max(); From e428dcc57e23cfb3dacce63990a1374d45f7be03 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Mar 2023 07:49:28 +0100 Subject: [PATCH 050/201] fix: ../src/slic3r/Utils/WxFontUtils.cpp:175:27: error: no member named 'IsFree' in 'wxFont' "IsFree(" << font.IsFree() << "), " << ~~~~ ^ 1 error generated. --- src/slic3r/Utils/WxFontUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index f209a1f7c..1c191606d 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -172,7 +172,7 @@ std::string WxFontUtils::store_wxFont(const wxFont &font) BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "' wx string get from GetNativeFontInfoDesc. wxFont " << "IsOk(" << font.IsOk() << "), " << "isNull(" << font.IsNull() << ")" << - "IsFree(" << font.IsFree() << "), " << + // "IsFree(" << font.IsFree() << "), " << // on MacOs is no function is free "IsFixedWidth(" << font.IsFixedWidth() << "), " << "IsUsingSizeInPixels(" << font.IsUsingSizeInPixels() << "), " << "Encoding(" << (int)font.GetEncoding() << "), " ; From 191f670dbd9842059fa83ca72301e253a5a573e5 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Mar 2023 14:25:06 +0100 Subject: [PATCH 051/201] Volume do not store angle of text any more. (it is stored inside of volume transformation and si calculated on the fly) --- src/libslic3r/Emboss.cpp | 18 ++- src/libslic3r/Emboss.hpp | 1 + src/libslic3r/TextConfiguration.hpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 191 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 16 +- src/slic3r/GUI/Jobs/EmbossJob.cpp | 7 + src/slic3r/GUI/Selection.cpp | 6 +- src/slic3r/GUI/SurfaceDrag.cpp | 16 +- src/slic3r/GUI/SurfaceDrag.hpp | 7 +- 9 files changed, 167 insertions(+), 103 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 000507779..65aa5a333 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1264,15 +1264,17 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, return result; } -void Emboss::apply_transformation(const FontProp &font_prop, - Transform3d &transformation) -{ - if (font_prop.angle.has_value()) { - double angle_z = *font_prop.angle; +void Emboss::apply_transformation(const FontProp &font_prop, Transform3d &transformation){ + apply_transformation(font_prop.angle, font_prop.distance, transformation); +} + +void Emboss::apply_transformation(const std::optional& angle, const std::optional& distance, Transform3d &transformation) { + if (angle.has_value()) { + double angle_z = *angle; transformation *= Eigen::AngleAxisd(angle_z, Vec3d::UnitZ()); } - if (font_prop.distance.has_value()) { - Vec3d translate = Vec3d::UnitZ() * (*font_prop.distance); + if (distance.has_value()) { + Vec3d translate = Vec3d::UnitZ() * (*distance); transformation.translate(translate); } } @@ -1583,7 +1585,7 @@ std::optional Emboss::calc_up(const Transform3d &tr, double up_limit) m.row(2) = normal; double det = m.determinant(); - return atan2(det, dot); + return -atan2(det, dot); } Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index d1ddbb1c3..fc0f0a0a3 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -193,6 +193,7 @@ namespace Emboss /// Z-rotation as angle to Y axis(FontProp::angle) /// In / Out transformation to modify by property void apply_transformation(const FontProp &font_prop, Transform3d &transformation); + void apply_transformation(const std::optional &angle, const std::optional &distance, Transform3d &transformation); /// /// Read information from naming table of font file diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index f303b17e5..1c1ce7756 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -49,10 +49,12 @@ struct FontProp // used for move over model surface // When not set value is zero and is not stored std::optional distance; // [in mm] - - // change up vector direction of font + + // Angle of rotation around emboss direction (Z axis) + // It is calculate on the fly from volume world transformation + // only StyleManager keep actual value for comparision with style // When not set value is zero and is not stored - std::optional angle; // [in radians] + std::optional angle; // [in radians] form -Pi to Pi // Parameter for True Type Font collections // Select index of font in collection diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 9a85eacb3..c9b493b98 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -325,9 +325,13 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) if (!m_dragging) return used; if (mouse_event.Dragging()) { - auto &angle_opt = m_volume->text_configuration->style.prop.angle; - if (!m_rotate_start_angle.has_value()) - m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f; + if (!m_rotate_start_angle.has_value()) { + // when m_rotate_start_angle is not set mean it is not Dragging + // when angle_opt is not set mean angle is Zero + const std::optional &angle_opt = m_style_manager.get_font_prop().angle; + m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f; + } + double angle = m_rotate_gizmo.get_angle(); angle -= PI / 2; // Grabber is upward @@ -339,18 +343,15 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) angle += *m_rotate_start_angle; // move to range <-M_PI, M_PI> priv::to_range_pi_pi(angle); - // propagate angle into property - angle_opt = static_cast(angle); - - // do not store zero - if (is_approx(*angle_opt, 0.f)) - angle_opt.reset(); // set into activ style assert(m_style_manager.is_active_font()); - if (m_style_manager.is_active_font()) + if (m_style_manager.is_active_font()) { + std::optional angle_opt; + if (!is_approx(angle, 0.)) + angle_opt = angle; m_style_manager.get_font_prop().angle = angle_opt; - + } } return used; } @@ -361,9 +362,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; + std::optional up_limit; + if (m_keep_up) up_limit = priv::up_limit; const Camera &camera = wxGetApp().plater()->get_camera(); bool was_dragging = m_surface_drag.has_value(); - bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager); + bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager, up_limit); bool is_dragging = m_surface_drag.has_value(); // End with surface dragging? @@ -387,6 +390,17 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) else if (was_dragging && is_dragging) { // update scale of selected volume --> should be approx the same calculate_scale(); + + // Recalculate angle for GUI + if (!m_keep_up) { + const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + assert(gl_volume != nullptr); + assert(m_style_manager.is_active_font()); + if (gl_volume == nullptr || !m_style_manager.is_active_font()) + return res; + + m_style_manager.get_style().prop.angle = calc_up(gl_volume->world_matrix(), priv::up_limit); + } } return res; } @@ -693,6 +707,13 @@ void GLGizmoEmboss::on_stop_dragging() // apply rotation m_parent.do_rotate(L("Text-Rotate")); + // Re-Calculate current angle of up vector + const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + assert(m_style_manager.is_active_font()); + assert(gl_volume != nullptr); + if (m_style_manager.is_active_font() && gl_volume != nullptr) + m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); + m_rotate_start_angle.reset(); // recalculate for surface cut @@ -723,32 +744,37 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() int count_letter_M_in_input = 12; cfg.input_width = letter_m_size.x * count_letter_M_in_input; GuiCfg::Translations &tr = cfg.translations; - tr.font = _u8L("Font"); - tr.size = _u8L("Height"); - tr.depth = _u8L("Depth"); + + tr.font = _u8L("Font"); + tr.height = _u8L("Height"); + tr.depth = _u8L("Depth"); + float max_text_width = std::max({ ImGui::CalcTextSize(tr.font.c_str()).x, - ImGui::CalcTextSize(tr.size.c_str()).x, + ImGui::CalcTextSize(tr.height.c_str()).x, ImGui::CalcTextSize(tr.depth.c_str()).x}); cfg.indent = static_cast(cfg.icon_width); cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space; - tr.use_surface = _u8L("Use surface"); - tr.char_gap = _u8L("Char gap"); - tr.line_gap = _u8L("Line gap"); - tr.boldness = _u8L("Boldness"); - tr.italic = _u8L("Skew ratio"); - tr.surface_distance = _u8L("Z-move"); - tr.angle = _u8L("Z-rot"); - tr.collection = _u8L("Collection"); + tr.use_surface = _u8L("Use surface"); + tr.char_gap = _u8L("Char gap"); + tr.line_gap = _u8L("Line gap"); + tr.boldness = _u8L("Boldness"); + tr.skew_ration = _u8L("Skew ratio"); + tr.from_surface = _u8L("From surface"); + tr.rotation = _u8L("Rotation"); + tr.keep_up = _u8L("Keep Up"); + tr.collection = _u8L("Collection"); + float max_advanced_text_width = std::max({ ImGui::CalcTextSize(tr.use_surface.c_str()).x, ImGui::CalcTextSize(tr.char_gap.c_str()).x, ImGui::CalcTextSize(tr.line_gap.c_str()).x, ImGui::CalcTextSize(tr.boldness.c_str()).x, - ImGui::CalcTextSize(tr.italic.c_str()).x, - ImGui::CalcTextSize(tr.surface_distance.c_str()).x, - ImGui::CalcTextSize(tr.angle.c_str()).x, + ImGui::CalcTextSize(tr.skew_ration.c_str()).x, + ImGui::CalcTextSize(tr.from_surface.c_str()).x, + ImGui::CalcTextSize(tr.rotation.c_str()).x, + ImGui::CalcTextSize(tr.keep_up.c_str()).x, ImGui::CalcTextSize(tr.collection.c_str()).x }); cfg.advanced_input_offset = max_advanced_text_width + 3 * space + cfg.indent; @@ -774,9 +800,9 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() + 2 * (cfg.icon_width + space); cfg.minimal_window_size = ImVec2(window_width, window_height); - // 6 = charGap, LineGap, Bold, italic, surfDist, angle + // 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera // 4 = 1px for fix each edit image of drag float - float advance_height = input_height * 8 + 8; + float advance_height = input_height * 9 + 8; cfg.minimal_window_size_with_advance = ImVec2(cfg.minimal_window_size.x, cfg.minimal_window_size.y + advance_height); @@ -867,9 +893,17 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } void GLGizmoEmboss::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); - ModelVolume *vol = get_selected_volume(selection); - // is same volume selected? - if (vol != nullptr && vol->id() == m_volume_id) + const GLVolume *gl_volume = get_selected_gl_volume(selection); + if (gl_volume == nullptr) + return reset_volume(); + + const ModelObjectPtrs &objects = selection.get_model()->objects; + ModelVolume *volume =get_model_volume(*gl_volume, objects); + if (volume == nullptr) + return reset_volume(); + + // is same volume as actual selected? + if (volume->id() == m_volume_id) return; // for changed volume notification is NOT valid @@ -877,30 +911,14 @@ void GLGizmoEmboss::set_volume_by_selection() // Do not use focused input value when switch volume(it must swith value) if (m_volume != nullptr && - m_volume != vol) // when update volume it changed id BUT not pointer + m_volume != volume) // when update volume it changed id BUT not pointer ImGuiWrapper::left_inputs(); - if (vol == nullptr) { - reset_volume(); - return; - } + // Is selected volume text volume? + const std::optional& tc_opt = volume->text_configuration; + if (!tc_opt.has_value()) + return reset_volume(); - // is select embossed volume? - set_volume(vol); - - // Check if user changed up vector by rotation or scale out of emboss gizmo - if (m_volume != nullptr) { - Transform3d world = selection.get_first_volume()->world_matrix(); - std::optional angle = calc_up(world, priv::up_limit); - m_volume->text_configuration->style.prop.angle = angle; - } -} - -bool GLGizmoEmboss::set_volume(ModelVolume *volume) -{ - assert(volume != nullptr); - const std::optional tc_opt = volume->text_configuration; - if (!tc_opt.has_value()) return false; const TextConfiguration &tc = *tc_opt; const EmbossStyle &style = tc.style; @@ -992,7 +1010,7 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) // The change of volume could show or hide part with setter on volume type if (m_volume == nullptr || - get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || + get_model_volume(m_volume_id, objects) == nullptr || (m_volume->get_object()->volumes.size() == 1) != (volume->get_object()->volumes.size() == 1)){ m_should_set_minimal_windows_size = true; @@ -1008,9 +1026,13 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) m_volume = volume; m_volume_id = volume->id(); + // Calculate current angle of up vector + assert(m_style_manager.is_active_font()); + if (m_style_manager.is_active_font()) + m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); + // calculate scale for height and depth inside of scaled object instance - calculate_scale(); - return true; + calculate_scale(); } void GLGizmoEmboss::reset_volume() @@ -1020,9 +1042,9 @@ void GLGizmoEmboss::reset_volume() m_volume = nullptr; m_volume_id.id = 0; - // TODO: check if it is neccessary to set default text - // Idea is to set default text when create object - set_default_text(); + + // No more need of current notification + remove_notification_not_valid_font(); } void GLGizmoEmboss::calculate_scale() { @@ -2677,7 +2699,7 @@ void GLGizmoEmboss::draw_height(bool use_inch) const float *stored = ((stored_style)? &stored_style->prop.size_in_mm : nullptr); const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm"); const std::string revert_text_size = _u8L("Revert text size."); - const std::string& name = m_gui_cfg->translations.size; + const std::string& name = m_gui_cfg->translations.height; if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) if (set_height()) process(); @@ -2915,7 +2937,7 @@ void GLGizmoEmboss::draw_advanced() // input italic auto def_skew = stored_style ? &stored_style->prop.skew : nullptr; - if (rev_slider(tr.italic, font_prop.skew, def_skew, _u8L("Undo letter's skew"), + if (rev_slider(tr.skew_ration, font_prop.skew, def_skew, _u8L("Undo letter's skew"), priv::limits.skew.gui.min, priv::limits.skew.gui.max, "%.2f", _L("Italic strength ratio"))){ if (!priv::Limits::apply(font_prop.skew, priv::limits.skew.values) || !m_volume->text_configuration->style.prop.skew.has_value() || @@ -2949,7 +2971,7 @@ void GLGizmoEmboss::draw_advanced() } min_distance *= ObjectManipulation::mm_to_in; max_distance *= ObjectManipulation::mm_to_in; - if (rev_slider(tr.surface_distance, distance_inch, def_distance, undo_move_tooltip, min_distance, max_distance, "%.3f in", move_tooltip)) { + if (rev_slider(tr.from_surface, distance_inch, def_distance, undo_move_tooltip, min_distance, max_distance, "%.3f in", move_tooltip)) { if (distance_inch.has_value()) { font_prop.distance = *distance_inch * ObjectManipulation::in_to_mm; } else { @@ -2958,7 +2980,7 @@ void GLGizmoEmboss::draw_advanced() is_moved = true; } } else { - if (rev_slider(tr.surface_distance, distance, def_distance, undo_move_tooltip, + if (rev_slider(tr.from_surface, distance, def_distance, undo_move_tooltip, min_distance, max_distance, "%.2f mm", move_tooltip)) is_moved = true; } @@ -2971,33 +2993,50 @@ void GLGizmoEmboss::draw_advanced() // slider for Clock-wise angle in degress // stored angle is optional CCW and in radians - std::optional &angle = font_prop.angle; - float prev_angle = angle.has_value() ? *angle : .0f; // Convert stored value to degress // minus create clock-wise roation from CCW - float angle_deg = angle.has_value() ? - static_cast(-(*angle) * 180 / M_PI) : .0f; + const std::optional &angle_opt = m_style_manager.get_font_prop().angle; + float angle = angle_opt.has_value() ? *angle_opt: 0.f; + float angle_deg = static_cast(-angle * 180 / M_PI); float def_angle_deg_val = (!stored_style || !stored_style->prop.angle.has_value()) ? 0.f : (*stored_style->prop.angle * -180 / M_PI); float* def_angle_deg = stored_style ? &def_angle_deg_val : nullptr; - if (rev_slider(tr.angle, angle_deg, def_angle_deg, _u8L("Undo rotation"), + if (rev_slider(tr.rotation, angle_deg, def_angle_deg, _u8L("Undo rotation"), priv::limits.angle.min, priv::limits.angle.max, u8"%.2f °", _L("Rotate text Clock-wise."))) { // convert back to radians and CCW - angle = -angle_deg * M_PI / 180.0; - priv::to_range_pi_pi(*angle); - if (is_approx(*angle, 0.f)) - angle.reset(); + float angle_rad = static_cast(-angle_deg * M_PI / 180.0); + priv::to_range_pi_pi(angle_rad); + + + float diff_angle = angle_rad - angle; + do_rotate(diff_angle); + + // calc angle after rotation + const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + assert(gl_volume != nullptr); + assert(m_style_manager.is_active_font()); + if (m_style_manager.is_active_font() && gl_volume != nullptr) + m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); - m_volume->text_configuration->style.prop.angle = angle; - float act_angle = angle.has_value() ? *angle : .0f; - do_rotate(act_angle - prev_angle); // recalculate for surface cut - if (font_prop.use_surface) process(); + if (font_prop.use_surface) + process(); } + ImGui::Text("%s", tr.keep_up.c_str()); + ImGui::SameLine(m_gui_cfg->advanced_input_offset); + if (ImGui::Checkbox("##keep_up", &m_keep_up)) { + if (m_keep_up) { + // copy angle to volume + m_volume->text_configuration->style.prop.angle = font_prop.angle; + } + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", _u8L("Keep text orientation during surface dragging.\nNot stable between horizontal and vertical alignment.").c_str()); + // when more collection add selector if (ff.font_file->infos.size() > 1) { ImGui::Text("%s", tr.collection.c_str()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 5ee2d396a..7e7c2aa16 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -83,8 +83,6 @@ private: void set_default_text(); void set_volume_by_selection(); - // load text configuration from volume into gizmo - bool set_volume(ModelVolume *volume); void reset_volume(); // create volume from text - main functionality @@ -198,7 +196,7 @@ private: struct Translations { std::string font; - std::string size; + std::string height; std::string depth; std::string use_surface; @@ -206,9 +204,10 @@ private: std::string char_gap; std::string line_gap; std::string boldness; - std::string italic; - std::string surface_distance; - std::string angle; + std::string skew_ration; + std::string from_surface; + std::string rotation; + std::string keep_up; std::string collection; }; Translations translations; @@ -286,7 +285,10 @@ private: static void init_truncated_names(Facenames &face_names, float max_width); // Text to emboss - std::string m_text; + std::string m_text; // Sequence of Unicode UTF8 symbols + + // When true keep up vector otherwise relative rotation + bool m_keep_up = true; // current selected volume // NOTE: Be carefull could be uninitialized (removed from Model) diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index bc80285ea..f5c315b30 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -502,6 +502,9 @@ void UpdateJob::update_volume(ModelVolume *volume, volume->calculate_convex_hull(); volume->get_object()->invalidate_bounding_box(); volume->text_configuration = text_configuration; + + // discard information about rotation, should not be stored in volume + volume->text_configuration->style.prop.angle.reset(); GUI_App &app = wxGetApp(); // may be move to input GLCanvas3D *canvas = app.plater()->canvas3D(); @@ -615,6 +618,10 @@ void priv::create_volume( volume->name = data.volume_name; // copy volume->text_configuration = data.text_configuration; // copy + + // discard information about rotation, should not be stored in volume + volume->text_configuration->style.prop.angle.reset(); + volume->set_transformation(trmat); // update printable state on canvas diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3e85d1d88..55a162719 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -3366,9 +3366,11 @@ void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& v ModelVolume *get_selected_volume(const Selection &selection) { - const GLVolume *vol_gl = get_selected_gl_volume(selection); + const GLVolume *gl_volume = get_selected_gl_volume(selection); + if (gl_volume == nullptr) + return nullptr; const ModelObjectPtrs &objects = selection.get_model()->objects; - return get_model_volume(*vol_gl, objects); + return get_model_volume(*gl_volume, objects); } const GLVolume *get_selected_gl_volume(const Selection &selection) diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 33f64c0c6..0f2f1706a 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -69,7 +69,8 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, const Camera &camera, std::optional &surface_drag, GLCanvas3D &canvas, - RaycastManager &raycast_manager) + RaycastManager &raycast_manager, + std::optional up_limit) { // Fix when leave window during dragging // Fix when click right button @@ -153,7 +154,10 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, Transform3d instance_tr = instance->get_matrix(); Transform3d instance_tr_inv = instance_tr.inverse(); Transform3d world_tr = instance_tr * volume_tr; - surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; + std::optional start_angle; + if (up_limit.has_value()) + start_angle = Emboss::calc_up(world_tr, *up_limit); + surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle}; // disable moving with object by mouse canvas.enable_moving(false); @@ -195,11 +199,11 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, Transform3d world_new = z_rotation * surface_drag->world; auto world_new_linear = world_new.linear(); - // Fix direction of up vector - { + // Fix direction of up vector to zero initial rotation + if(up_limit.has_value()){ Vec3d z_world = world_new_linear.col(2); z_world.normalize(); - Vec3d wanted_up = Emboss::suggest_up(z_world); + Vec3d wanted_up = Emboss::suggest_up(z_world, *up_limit); Vec3d y_world = world_new_linear.col(1); auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); @@ -229,7 +233,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, volume_new = volume_new * (*tc.fix_3mf_tr); // apply move in Z direction and rotation by up vector - Emboss::apply_transformation(tc.style.prop, volume_new); + Emboss::apply_transformation(surface_drag->start_angle, tc.style.prop.distance, volume_new); } // Update transformation for all instances diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp index a3765f86b..bb2600c28 100644 --- a/src/slic3r/GUI/SurfaceDrag.hpp +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -34,6 +34,9 @@ struct SurfaceDrag // condition for raycaster RaycastManager::AllowVolumes condition; + // initial rotation in Z axis of volume + std::optional start_angle; + // Flag whether coordinate hit some volume bool exist_hit = true; }; @@ -48,12 +51,14 @@ struct SurfaceDrag /// Contain gl_volumes and selection /// AABB trees for raycast in object /// Refresh state inside of function +/// When set than use correction of up vector /// True when event is processed otherwise false bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, const Camera &camera, std::optional &surface_drag, GLCanvas3D &canvas, - RaycastManager &raycast_manager); + RaycastManager &raycast_manager, + std::optional up_limit = {}); /// /// Calculate translation of volume onto surface of model From 8e8c5652b96dc4ab7d7739bfc2b89b9ae729ef61 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Mar 2023 16:40:48 +0100 Subject: [PATCH 052/201] Fix for: ../src/slic3r/GUI/IconManager.cpp:38:57: error: 'fabs' is not a member of 'std'; did you mean 'abs'? 38 | unsigned int width = static_cast(std::fabs(std::round(size.x))); | ^~~~ | abs ../src/slic3r/GUI/IconManager.cpp:38:67: error: 'round' is not a member of 'std'; did you mean 'std::chrono::round'? 38 | unsigned int width = static_cast(std::fabs(std::round(size.x))); | ^~~~~ --- src/slic3r/GUI/IconManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 78ae673ef..1974e917c 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -1,5 +1,5 @@ #include "IconManager.hpp" - +#include #include using namespace Slic3r::GUI; @@ -35,7 +35,7 @@ std::vector IconManager::init(const std::vector // only rectangle are supported assert(size.x == size.y); // no subpixel supported - unsigned int width = static_cast(std::fabs(std::round(size.x))); + unsigned int width = static_cast(std::abs(std::round(size.x))); assert(size.x == static_cast(width)); // state order has to match the enum IconState @@ -171,7 +171,7 @@ void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_ return; } - ImTextureID id = (void *) icon.tex_id; + ImTextureID id = (void *) icon.tex_id; const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); } From 49bd17a0af7801926a1553cfea731a14507e19db Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 8 Mar 2023 15:10:36 +0100 Subject: [PATCH 053/201] Less agressive warning about bad visualization inside of text input --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 86 ++++++++++++------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 6deda777e..88ccde4bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -223,6 +223,7 @@ enum class IconType : unsigned { unbold, system_selector, open_file, + exclamation, // automatic calc of icon's count _count }; @@ -1283,6 +1284,8 @@ void GLGizmoEmboss::draw_window() #endif // ALLOW_FLOAT_WINDOW } +#include "imgui/imgui_internal.h" // scroll bar existence + void GLGizmoEmboss::draw_text_input() { auto create_range_text_prep = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() { @@ -1312,75 +1315,69 @@ void GLGizmoEmboss::draw_text_input() if (exist_font) ImGui::PushFont(imgui_font); // show warning about incorrectness view of font - std::string warning; - std::string tool_tip; + std::string warning_tool_tip; if (!exist_font) { - warning = _u8L("Can't write text by selected font."); - tool_tip = _u8L("Try to choose another font."); + warning_tool_tip = _u8L("Can't write text by selected font.Try to choose another font."); } else { - std::string who; - auto append_warning = [&who, &tool_tip](std::string w, std::string t) { - if (!w.empty()) { - if (!who.empty()) who += " & "; - who += w; - } - if (!t.empty()) { - if (!tool_tip.empty()) tool_tip += "\n"; - tool_tip += t; - } + auto append_warning = [&warning_tool_tip](std::string t) { + if (!warning_tool_tip.empty()) + warning_tool_tip += "\n"; + warning_tool_tip += t; }; - if (priv::is_text_empty(m_text)) append_warning(_u8L("Empty"), _u8L("Embossed text can NOT contain only white spaces.")); + if (priv::is_text_empty(m_text)) + append_warning(_u8L("Embossed text can NOT contain only white spaces.")); if (m_text_contain_unknown_glyph) - append_warning(_u8L("Bad symbol"), _u8L("Text contain character glyph (represented by '?') unknown by font.")); + append_warning(_u8L("Text contain character glyph (represented by '?') unknown by font.")); const FontProp &prop = m_style_manager.get_font_prop(); - if (prop.skew.has_value()) append_warning(_u8L("Skew"), _u8L("Unsupported visualization of font skew for text input.")); - if (prop.boldness.has_value()) append_warning(_u8L("Boldness"), _u8L("Unsupported visualization of font boldness for text input.")); + if (prop.skew.has_value()) append_warning(_u8L("Text input do not show font skew.")); + if (prop.boldness.has_value()) append_warning(_u8L("Text input do not show font boldness.")); if (prop.line_gap.has_value()) - append_warning(_u8L("Line gap"), _u8L("Unsupported visualization of gap between lines inside text input.")); + append_warning(_u8L("Text input do not show gap between lines.")); auto &ff = m_style_manager.get_font_file_with_cache(); float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale); if (imgui_size > StyleManager::max_imgui_font_size) - append_warning(_u8L("Too tall"), _u8L("Diminished font height inside text input.")); + append_warning(_u8L("Too tall, diminished font height inside text input.")); if (imgui_size < StyleManager::min_imgui_font_size) - append_warning(_u8L("Too small"), _u8L("Enlarged font height inside text input.")); - if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who); + append_warning(_u8L("Too small, enlarged font height inside text input.")); } - - // add border around input when warning appears - ScopeGuard input_border_sg; - if (!warning.empty()) { - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImGuiWrapper::COL_ORANGE_LIGHT); - input_border_sg.closure = []() { ImGui::PopStyleColor(); ImGui::PopStyleVar(); }; - } - + // flag for extend font ranges if neccessary // ranges can't be extend during font is activ(pushed) std::string range_text; float window_height = ImGui::GetWindowHeight(); float minimal_height = get_minimal_window_size().y; float extra_height = window_height - minimal_height; - ImVec2 text_size(m_gui_cfg->text_size.x, - m_gui_cfg->text_size.y + extra_height); + ImVec2 input_size(m_gui_cfg->text_size.x, m_gui_cfg->text_size.y + extra_height); const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll; - if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) { + if (ImGui::InputTextMultiline("##Text", &m_text, input_size, flags)) { process(); range_text = create_range_text_prep(); } if (exist_font) ImGui::PopFont(); - if (!warning.empty()) { - if (ImGui::IsItemHovered() && !tool_tip.empty()) - ImGui::SetTooltip("%s", tool_tip.c_str()); + // warning tooltip has to be with default font + if (!warning_tool_tip.empty()) { + // Multiline input has hidden window for scrolling + ImGuiWindow *input = ImGui::GetCurrentWindow()->DC.ChildWindows.front(); + + const ImGuiStyle &style = ImGui::GetStyle(); + float scrollbar_width = (input->ScrollbarY) ? style.ScrollbarSize : 0.f; + float scrollbar_height = (input->ScrollbarX) ? style.ScrollbarSize : 0.f; + + bool hovered = ImGui::IsItemHovered(); + if (hovered) + ImGui::SetTooltip("%s", warning_tool_tip.c_str()); + ImVec2 cursor = ImGui::GetCursorPos(); float width = ImGui::GetContentRegionAvailWidth(); - ImVec2 size = ImGui::CalcTextSize(warning.c_str()); - ImVec2 padding = ImGui::GetStyle().FramePadding; - ImGui::SetCursorPos(ImVec2(width - size.x + padding.x, - cursor.y - size.y - padding.y)); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, warning); + const ImVec2& padding = style.FramePadding; + ImVec2 icon_pos(width - m_gui_cfg->icon_width - scrollbar_width + padding.x, + cursor.y - m_gui_cfg->icon_width - scrollbar_height - 2*padding.y); + + ImGui::SetCursorPos(icon_pos); + draw(get_icon(m_icons, IconType::exclamation, IconState::hovered)); ImGui::SetCursorPos(cursor); } @@ -2376,7 +2373,7 @@ bool GLGizmoEmboss::draw_italic_button() const wxFont &wx_font = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font.IsOk() || !ff.has_value()) { - draw(*m_icons[(int) IconType::italic][(int)IconState::disabled]); + draw(get_icon(m_icons, IconType::italic, IconState::disabled)); return false; } @@ -3316,7 +3313,8 @@ void GLGizmoEmboss::init_icons() "make_bold.svg", "make_unbold.svg", "search.svg", - "open.svg" + "open.svg", + "exclamation.svg" }; assert(filenames.size() == static_cast(IconType::_count)); std::string path = resources_dir() + "/icons/"; From 750e374357ece077f4bd1415e94c8f2ad01b8dbf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 10 Mar 2023 09:51:51 +0100 Subject: [PATCH 054/201] Follow-up of f708d9fcb9b84ca98474475152cba45cc793c218 - Fixed rendering of printbed reference axes when any gizmo is open --- src/slic3r/GUI/3DBed.cpp | 14 ++++---------- src/slic3r/GUI/3DBed.hpp | 9 +++------ src/slic3r/GUI/GLCanvas3D.cpp | 19 +++++++++++++------ src/slic3r/GUI/GLCanvas3D.hpp | 4 +++- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index bef4c0d89..b4f0dd9d8 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -157,26 +157,23 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } -void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture) +void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_texture) { - render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes, show_texture, false); + render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_texture, false); } void Bed3D::render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor) { - render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, false, false, true); + render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, false, true); } void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, - bool show_axes, bool show_texture, bool picking) + bool show_texture, bool picking) { m_scale_factor = scale_factor; glsafe(::glEnable(GL_DEPTH_TEST)); - if (show_axes) - render_axes(); - m_model.model.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR); switch (m_type) @@ -366,9 +363,6 @@ std::tuple Bed3D::detect_type(const Point void Bed3D::render_axes() { - if (!m_show_axes) - return; - if (m_build_volume.valid()) #if ENABLE_WORLD_COORDINATE m_axes.render(Transform3d::Identity(), 0.25f); diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 36aefef79..031a8e8a3 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -84,7 +84,6 @@ private: #endif // ENABLE_WORLD_COORDINATE float m_scale_factor{ 1.0f }; - bool m_show_axes{ true }; public: Bed3D() = default; @@ -112,9 +111,8 @@ public: bool contains(const Point& point) const; Point point_projection(const Point& point) const; - void toggle_show_axes() { m_show_axes = !m_show_axes; } - - void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture); + void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_texture); + void render_axes(); void render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor); private: @@ -125,8 +123,7 @@ private: void init_contourlines(); static std::tuple detect_type(const Pointfs& shape); void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, - bool show_axes, bool show_texture, bool picking); - void render_axes(); + bool show_texture, bool picking); void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture); void render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 05cff51fa..1f2f668c3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1573,8 +1573,10 @@ void GLCanvas3D::render() _render_objects(GLVolumeCollection::ERenderType::Opaque); _render_sla_slices(); _render_selection(); + if (m_show_bed_axes) + _render_bed_axes(); if (is_looking_downward) - _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), false, true); + _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), false); if (!m_main_toolbar.is_enabled()) _render_gcode(); _render_objects(GLVolumeCollection::ERenderType::Transparent); @@ -1597,7 +1599,7 @@ void GLCanvas3D::render() _render_selection_sidebar_hints(); _render_current_gizmo(); if (!is_looking_downward) - _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), true, true); + _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), true); #if ENABLE_RAYCAST_PICKING_DEBUG if (m_picking_enabled && !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging()) @@ -2355,7 +2357,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #else /* __APPLE__ */ case WXK_CONTROL_D: #endif /* __APPLE__ */ - m_bed.toggle_show_axes(); + m_show_bed_axes = !m_show_bed_axes; m_dirty = true; break; #ifdef __APPLE__ @@ -4423,7 +4425,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const glsafe(::glDisable(GL_DEPTH_TEST)); if (thumbnail_params.show_bed) - _render_bed(view_matrix, projection_matrix, !camera.is_looking_downward(), false); + _render_bed(view_matrix, projection_matrix, !camera.is_looking_downward()); // restore background color if (thumbnail_params.transparent_background) @@ -5477,7 +5479,7 @@ void GLCanvas3D::_render_background() glsafe(::glEnable(GL_DEPTH_TEST)); } -void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes) +void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom) { float scale_factor = 1.0; #if ENABLE_RETINA_GL @@ -5491,7 +5493,12 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d& && m_gizmos.get_current_type() != GLGizmosManager::Seam && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation); - m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_axes, show_texture); + m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_texture); +} + +void GLCanvas3D::_render_bed_axes() +{ + m_bed.render_axes(); } void GLCanvas3D::_render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index e91a727c5..ad33a5d68 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -522,6 +522,7 @@ private: ECursorType m_cursor_type; GLSelectionRectangle m_rectangle_selection; std::vector m_hover_volume_idxs; + bool m_show_bed_axes{ true }; // Following variable is obsolete and it should be safe to remove it. // I just don't want to do it now before a release (Lukas Matena 24.3.2019) @@ -984,7 +985,8 @@ private: void _picking_pass(); void _rectangular_selection_picking_pass(); void _render_background(); - void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes); + void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); + void _render_bed_axes(); void _render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); void _render_objects(GLVolumeCollection::ERenderType type); void _render_gcode(); From f6445eefe3b2bb429fdb569267935c53f438290c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 10 Mar 2023 11:49:53 +0100 Subject: [PATCH 055/201] Fix a memory leak in placeholder parser --- src/libslic3r/PlaceholderParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 2dd5e8f8e..5235fd72e 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -234,6 +234,7 @@ namespace client delete m_data.s; m_type = TYPE_EMPTY; } + ~expr() { reset(); } enum Type { TYPE_EMPTY = 0, From fd890888280abf1ad7a1d4e1ee12d32157fadede Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 10 Mar 2023 13:08:45 +0100 Subject: [PATCH 056/201] Fix for SPE-1494 - When inches is enabled, then entered negative or zero value was transformed to the wrong one --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index c16d77751..2a172b91f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1395,8 +1395,9 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double if (new_value > 0.0) change_size_value(axis, new_value); else { - new_value = m_cache.size(axis); - m_cache.size(axis) = 0.0; + Vec3d& size = m_imperial_units ? m_cache.size_inches : m_cache.size; + new_value = size(axis); + size(axis) = 0.0; m_cache.size_rounded(axis) = DBL_MAX; change_size_value(axis, new_value); } From 035997a0495ce7e7149a6f01a00faae4aed30817 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 10 Mar 2023 13:47:50 +0100 Subject: [PATCH 057/201] Deleted workaround related to UA localization --- src/slic3r/GUI/GUI_App.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0c4953e3a..076bef54b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2305,18 +2305,6 @@ bool GUI_App::load_language(wxString language, bool initial) // Override language at the active wxTranslations class (which is stored in the active m_wxLocale) // to load possibly different dictionary, for example, load Czech dictionary for Slovak language. wxTranslations::Get()->SetLanguage(language_dict); - { - // ysFIXME after fix for wxWidgets issue (https://github.com/wxWidgets/wxWidgets/issues/23210) - // UKR Localization specific workaround till the wxWidgets doesn't fixed: - // From wxWidgets 3.1.6 calls setlocation(0, wxInfoLanguage->LocaleTag), see (https://github.com/prusa3d/wxWidgets/commit/deef116a09748796711d1e3509965ee208dcdf0b#diff-7de25e9a71c4dce61bbf76492c589623d5b93fd1bb105ceaf0662075d15f4472), - // where LocaleTag is a Tag of locale in BCP 47 - like notation. - // For Ukrainian Language LocaleTag is "uk". - // But setlocale(0, "uk") returns "English_United Kingdom.1252" instead of "uk", - // and, as a result, locales are set to English_United Kingdom - - if (language_info->CanonicalName == "uk") - setlocale(0, language_info->GetCanonicalWithRegion().data()); - } m_wxLocale->AddCatalog(SLIC3R_APP_KEY); m_imgui->set_language(into_u8(language_info->CanonicalName)); //FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only. From c2fe61261dd4490b63114c8154575e036617c9ec Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 10 Mar 2023 15:21:20 +0100 Subject: [PATCH 058/201] Export STL - check path extension in lower case. #10000 --- src/slic3r/GUI/Plater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 905849b8a..22ce86c9f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6503,9 +6503,9 @@ void Plater::export_stl_obj(bool extended, bool selection_only) } } - if (path.EndsWith(".stl")) + if (path.Lower().EndsWith(".stl")) Slic3r::store_stl(path_u8.c_str(), &mesh, true); - else if (path.EndsWith(".obj")) + else if (path.Lower().EndsWith(".obj")) Slic3r::store_obj(path_u8.c_str(), &mesh); // p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path)); } From 2f9c98311498e7f312157bbf0090447019609fd1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Mar 2023 16:56:29 +0100 Subject: [PATCH 059/201] Fix of wrong use of GUI::format --- src/slic3r/GUI/FileArchiveDialog.cpp | 6 +++--- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 7337258cb..fc2e27bf8 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -206,7 +206,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar reduce_stack(stack, struct_size); } if (!file.has_extension() && stack.size() == struct_size) - stack.push_back(avc->get_model()->AddFile((stack.empty() ? std::shared_ptr(nullptr) : stack.back()), GUI::format_wxstr(file.filename().string()), true)); // filename string to wstring? + stack.push_back(avc->get_model()->AddFile((stack.empty() ? std::shared_ptr(nullptr) : stack.back()), boost::nowide::widen(file.filename().string()), true)); // filename string to wstring? return struct_size + 1; }; @@ -223,7 +223,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar path = boost::filesystem::path(extra.substr(0, extra_size)); } else { wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = GUI::format(wname); + std::string name = boost::nowide::narrow(wname); path = boost::filesystem::path(name); } assert(!path.empty()); @@ -247,7 +247,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar if (!stack.empty()) parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files - m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? entry_count++; } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22ce86c9f..defc8be3f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5576,7 +5576,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(&archive, i, &stat)) { wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = GUI::format(wname); + std::string name = boost::nowide::narrow(wname); fs::path archive_path(name); std::string extra(1024, 0); From 62b84b71128409737ba59d1585939b1974a12782 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Mar 2023 10:34:00 +0100 Subject: [PATCH 060/201] Fix arrange contour cache not addressing last segment - Only when shape closure type was OPEN, which it is for ExPolygon - Also remove duplicated first corner --- .../include/libnest2d/geometry_traits.hpp | 2 +- .../include/libnest2d/placers/nfpplacer.hpp | 33 ++++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 134ec73a0..a179e4c94 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -871,7 +871,7 @@ template auto rcend(const P& p) -> decltype(_backward(cbegin(p))) template TPoint

front(const P& p) { return *shapelike::cbegin(p); } template TPoint

back (const P& p) { - return *backward(shapelike::cend(p)); + return *std::prev(shapelike::cend(p)); } // Optional, does nothing by default diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 5b5311d90..a17d54982 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -157,26 +157,34 @@ template class EdgeCache { void createCache(const RawShape& sh) { { // For the contour - auto first = shapelike::cbegin(sh); - auto next = std::next(first); - auto endit = shapelike::cend(sh); + auto first = sl::cbegin(sh); + auto endit = sl::cend(sh); + auto next = first == endit ? endit : std::next(first); - contour_.distances.reserve(shapelike::contourVertexCount(sh)); + contour_.distances.reserve(sl::contourVertexCount(sh)); while(next != endit) { contour_.emap.emplace_back(*(first++), *(next++)); contour_.full_distance += length(contour_.emap.back()); contour_.distances.emplace_back(contour_.full_distance); } + + if constexpr (ClosureTypeV == Closure::OPEN) { + if (sl::contourVertexCount(sh) > 0) { + contour_.emap.emplace_back(sl::back(sh), sl::front(sh)); + contour_.full_distance += length(contour_.emap.back()); + contour_.distances.emplace_back(contour_.full_distance); + } + } } for(auto& h : shapelike::holes(sh)) { // For the holes - auto first = h.begin(); - auto next = std::next(first); - auto endit = h.end(); + auto first = sl::cbegin(h); + auto endit = sl::cend(h); + auto next = first == endit ? endit :std::next(first); ContourCache hc; - hc.distances.reserve(endit - first); + hc.distances.reserve(sl::contourVertexCount(h)); while(next != endit) { hc.emap.emplace_back(*(first++), *(next++)); @@ -184,6 +192,14 @@ template class EdgeCache { hc.distances.emplace_back(hc.full_distance); } + if constexpr (ClosureTypeV == Closure::OPEN) { + if (sl::contourVertexCount(h) > 0) { + hc.emap.emplace_back(sl::back(sh), sl::front(sh)); + hc.full_distance += length(hc.emap.back()); + hc.distances.emplace_back(hc.full_distance); + } + } + holes_.emplace_back(std::move(hc)); } } @@ -206,7 +222,6 @@ template class EdgeCache { contour_.corners.reserve(N / S + 1); contour_.corners.emplace_back(0.0); auto N_1 = N-1; - contour_.corners.emplace_back(0.0); for(size_t i = 0; i < N_1; i += S) { contour_.corners.emplace_back( contour_.distances.at(i) / contour_.full_distance); From eb31dcec4488a3137ee217a73e4438b4138ccbd4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Mar 2023 11:13:33 +0100 Subject: [PATCH 061/201] Fixed crash when deleting 2nd instance of object --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 2a172b91f..415f008fb 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -707,6 +707,12 @@ void ObjectManipulation::update_ui_from_settings() void ObjectManipulation::update_settings_value(const Selection& selection) { + if (selection.is_empty()) { + // No selection, reset the cache. + reset_settings_value(); + return; + } + m_new_move_label_string = L("Position"); m_new_rotate_label_string = L("Rotation"); m_new_scale_label_string = L("Scale factors"); @@ -836,11 +842,6 @@ void ObjectManipulation::update_settings_value(const Selection& selection) #endif // ENABLE_WORLD_COORDINATE m_new_enabled = true; } - else { - // No selection, reset the cache. -// assert(selection.is_empty()); - reset_settings_value(); - } } void ObjectManipulation::update_if_dirty() From 1154f51aabcf581bcd6a7123f699ee1924973e2a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Mar 2023 13:52:21 +0100 Subject: [PATCH 062/201] Fix failing libnest2d tests Fix the test itself --- tests/libnest2d/libnest2d_tests_main.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp index 97c7ef99d..f6df83b77 100644 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ b/tests/libnest2d/libnest2d_tests_main.cpp @@ -1024,9 +1024,15 @@ TEST_CASE("pointOnPolygonContour", "[Geometry]") { REQUIRE(getX(first) == getX(ecache.coords(0))); REQUIRE(getY(first) == getY(ecache.coords(0))); - auto last = *std::prev(input.end()); - REQUIRE(getX(last) == getX(ecache.coords(1.0))); - REQUIRE(getY(last) == getY(ecache.coords(1.0))); + if constexpr (ClosureTypeV == Closure::CLOSED) { + auto last = *std::prev(input.end()); + REQUIRE(getX(last) == getX(ecache.coords(1.0))); + REQUIRE(getY(last) == getY(ecache.coords(1.0))); + } else { + auto last = *input.begin(); + REQUIRE(getX(last) == getX(ecache.coords(1.0))); + REQUIRE(getY(last) == getY(ecache.coords(1.0))); + } for(int i = 0; i <= 100; i++) { auto v = ecache.coords(i*(0.01)); From b0a8757b12b02b97a3142851449e9d6e00927536 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 13 Mar 2023 16:31:48 +0100 Subject: [PATCH 063/201] CutGizmo: Use isApprox() instead of "==" operator for Vec3d values --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3ba67e60e..e9b6fa694 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -799,7 +799,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() void GLGizmoCut3D::render_cut_line() { - if (!cut_line_processing() || m_line_end == Vec3d::Zero()) + if (!cut_line_processing() || m_line_end.isApprox(Vec3d::Zero())) return; glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1130,7 +1130,7 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) rotation[m_hover_id] = theta; const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation); - const bool update_tbb = m_rotation_m.rotation() != rotation_tmp.rotation(); + const bool update_tbb = !m_rotation_m.rotation().isApprox(rotation_tmp.rotation()); m_rotation_m = rotation_tmp; if (update_tbb) m_transformed_bounding_box = transformed_bounding_box(m_plane_center, m_rotation_m); @@ -1262,7 +1262,7 @@ void GLGizmoCut3D::update_bb() const BoundingBoxf3 box = bounding_box(); if (!box.defined) return; - if (m_max_pos != box.max || m_min_pos != box.min) { + if (!m_max_pos.isApprox(box.max) || !m_min_pos.isApprox(box.min)) { m_bounding_box = box; @@ -1679,7 +1679,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) const bool has_connectors = !connectors.empty(); - const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center == m_plane_center; + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center); m_imgui->disabled_begin(is_cut_plane_init); wxString act_name = _L("Reset cutting plane"); if (render_reset_button("cut_plane", into_u8(act_name))) { @@ -2247,7 +2247,7 @@ void GLGizmoCut3D::update_connector_shape() bool GLGizmoCut3D::cut_line_processing() const { - return m_line_beg != Vec3d::Zero(); + return !m_line_beg.isApprox(Vec3d::Zero()); } void GLGizmoCut3D::discard_cut_line_processing() From 993d6bd561baf57ad2e36c247a39759e2742b666 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Mar 2023 11:11:51 +0100 Subject: [PATCH 064/201] Fixed a hang in tree supports, caused by integer overflow on coord_t. Fixes #10048 --- src/libslic3r/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 2ce404161..240bcf7d1 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -1451,7 +1451,7 @@ static void generate_initial_areas( // As a circle is round this length is identical for every axis as long as the 90 degrees angle between both remains. const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ? config.min_radius / 2 : - sqrt(sqr(config.min_radius) - sqr(config.min_radius - config.support_line_width / 2)); + scale_(sqrt(sqr(unscale(config.min_radius)) - sqr(unscale(config.min_radius - config.support_line_width / 2)))); // Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all. //FIXME Vojtech: This is not sufficient for support enforcers to work. //FIXME There is no account for the support overhang angle. From f4e44f975069bcf08531d0d15e9b793f85d5ea41 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 8 Mar 2023 18:42:27 +0100 Subject: [PATCH 065/201] rework of bridiging over sparse infill in progress --- src/libslic3r/Fill/Fill.cpp | 17 +-- src/libslic3r/Layer.hpp | 7 +- src/libslic3r/Print.hpp | 6 +- src/libslic3r/PrintObject.cpp | 194 ++++++++++++++++++++++++++++++---- 4 files changed, 187 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 02f613c15..8ca0199d0 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -486,14 +486,6 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: size_t first_object_layer_id = this->object()->get_layer(0)->id(); for (SurfaceFill &surface_fill : surface_fills) { - //skip patterns for which additional input is nullptr - switch (surface_fill.params.pattern) { - case ipLightning: if (lightning_generator == nullptr) continue; break; - case ipAdaptiveCubic: if (adaptive_fill_octree == nullptr) continue; break; - case ipSupportCubic: if (support_fill_octree == nullptr) continue; break; - default: break; - } - // Create the filler object. std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); @@ -647,7 +639,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: #endif } -Polylines Layer::generate_sparse_infill_polylines_for_anchoring() const +Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) const { std::vector surface_fills = group_fills(*this); const Slic3r::BoundingBox bbox = this->object()->bounding_box(); @@ -656,14 +648,13 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring() const Polylines sparse_infill_polylines{}; for (SurfaceFill &surface_fill : surface_fills) { - // skip patterns for which additional input is nullptr switch (surface_fill.params.pattern) { case ipLightning: continue; break; - case ipAdaptiveCubic: continue; break; - case ipSupportCubic: continue; break; case ipCount: continue; break; case ipSupportBase: continue; break; case ipEnsuring: continue; break; + case ipAdaptiveCubic: + case ipSupportCubic: case ipRectilinear: case ipMonotonic: case ipMonotonicLines: @@ -688,7 +679,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring() const f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; - // f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; + f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; f->print_config = &this->object()->print()->config(); f->print_object_config = &this->object()->config(); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index a59c029b8..0cba55d3c 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -368,8 +368,11 @@ public: void make_perimeters(); // Phony version of make_fills() without parameters for Perl integration only. void make_fills() { this->make_fills(nullptr, nullptr, nullptr); } - void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator); - Polylines generate_sparse_infill_polylines_for_anchoring() const; + void make_fills(FillAdaptive::Octree *adaptive_fill_octree, + FillAdaptive::Octree *support_fill_octree, + FillLightning::Generator *lightning_generator); + Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree, + FillAdaptive::Octree *support_fill_octree) const; void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 3795c2449..df2bde469 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Print_hpp_ #define slic3r_Print_hpp_ +#include "Fill/FillAdaptive.hpp" #include "PrintBase.hpp" #include "BoundingBox.hpp" @@ -385,7 +386,8 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); - std::pair prepare_adaptive_infill_data(); + std::pair prepare_adaptive_infill_data( + const std::vector>& surfaces_w_bottom_z) const; FillLightning::GeneratorPtr prepare_lightning_infill_data(); // XYZ in scaled coordinates @@ -410,6 +412,8 @@ private: // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; + + std::pair adaptive_fill_octrees; }; struct WipeTowerData diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4ddb36b9e..33029aac5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -37,7 +37,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -400,7 +402,8 @@ void PrintObject::infill() if (this->set_started(posInfill)) { m_print->set_status(45, L("making infill")); - auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); + const auto& adaptive_fill_octree = this->adaptive_fill_octrees.first; + const auto& support_fill_octree = this->adaptive_fill_octrees.second; auto lightning_generator = this->prepare_lightning_infill_data(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; @@ -509,7 +512,8 @@ void PrintObject::estimate_curled_extrusions() } } -std::pair PrintObject::prepare_adaptive_infill_data() +std::pair PrintObject::prepare_adaptive_infill_data( + const std::vector> &surfaces_w_bottom_z) const { using namespace FillAdaptive; @@ -523,22 +527,18 @@ std::pair PrintObject::prepare its_transform(mesh, to_octree * this->trafo_centered(), true); // Triangulate internal bridging surfaces. - std::vector> overhangs(this->layers().size()); - tbb::parallel_for( - tbb::blocked_range(0, int(m_layers.size()) - 1), - [this, &to_octree, &overhangs](const tbb::blocked_range &range) { - std::vector &out = overhangs[range.begin()]; - for (int idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - m_print->throw_if_canceled(); - const Layer *layer = this->layers()[idx_layer]; - for (const LayerRegion *layerm : layer->regions()) - for (const Surface &surface : layerm->fill_surfaces()) - if (surface.surface_type == stInternalBridge) - append(out, triangulate_expolygon_3d(surface.expolygon, layer->bottom_z())); - } - for (Vec3d &p : out) - p = (to_octree * p).eval(); - }); + std::vector> overhangs(surfaces_w_bottom_z.size()); + tbb::parallel_for(tbb::blocked_range(0, surfaces_w_bottom_z.size()), + [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range &range) { + for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { + std::vector &out = overhangs[surface_idx]; + m_print->throw_if_canceled(); + append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon, + surfaces_w_bottom_z[surface_idx].second)); + for (Vec3d &p : out) + p = (to_octree * p).eval(); + } + }); // and gather them. for (size_t i = 1; i < overhangs.size(); ++ i) append(overhangs.front(), std::move(overhangs[i])); @@ -1582,9 +1582,9 @@ void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info(); - struct ModifiedSurface + struct CandidateSurface { - ModifiedSurface(const Surface *original_surface, Polygons new_polys, const LayerRegion *region, double bridge_angle) + CandidateSurface(const Surface *original_surface, Polygons new_polys, const LayerRegion *region, double bridge_angle) : original_surface(original_surface), new_polys(new_polys), region(region), bridge_angle(bridge_angle) {} const Surface *original_surface; @@ -1593,9 +1593,161 @@ void PrintObject::bridge_over_infill() double bridge_angle; }; + tbb::concurrent_vector candidate_surfaces; + + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), + &candidate_surfaces](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + const Layer *layer = po->get_layer(lidx); + if (layer->lower_layer == nullptr) { + continue; + } + auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); + Polygons internal_area = shrink(to_polygons(layer->lower_layer->lslices), 4 * spacing); + Polygons lower_layer_solids; + for (const LayerRegion *region : layer->lower_layer->regions()) { + bool has_low_density = region->region().config().fill_density.value < 100; + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type == stInternal && has_low_density) { + Polygons p = to_polygons(surface.expolygon); + lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); + } + } + } + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + internal_area = diff(internal_area, lower_layer_solids); + + for (const LayerRegion *region : layer->regions()) { + SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); + for (const Surface *s : region_internal_solids) { + Polygons away_from_perimeter = intersection(to_polygons(s->expolygon), internal_area); + if (!away_from_perimeter.empty()) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(away_from_perimeter, 5 * spacing)); + candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + } + } + } + } + }); + + std::map> surfaces_by_layer; + std::vector> surfaces_w_bottom_z; + for (const CandidateSurface& c : candidate_surfaces) { + surfaces_by_layer[c.region->layer()->id()].push_back(c); + surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z()); + } + + this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + + std::map infill_lines; + std::vector layers_to_generate_infill; + for (const auto& pair : surfaces_by_layer) { + assert(pair.first > 0); + infill_lines[pair.first-1] = {}; + layers_to_generate_infill.push_back(pair.first-1); + } + + tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), + &infill_lines](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + infill_lines.at( + lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), + po->adaptive_fill_octrees.second.get()); + } + }); + + std::vector> jobs; + for (auto pair : surfaces_by_layer) { + if (jobs.empty() || jobs.back().second < pair.first) { + jobs.emplace_back(pair.first, pair.first + 1); + } else { + jobs.back().second = pair.first + 1; + } + } + + auto gahter_lower_layers_sparse_infill = [](const PrintObject *po, int lidx, float target_flow_height) { + // Gather lower layers sparse infill areas, to depth defined by used bridge flow + Polygons lower_layers_sparse_infill{}; + Polygons special_infill{}; + Polygons not_sparse_infill{}; + double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; + for (int i = int(lidx) - 1; i >= 0; --i) { + // Stop iterating if layer is lower than bottom_z. + if (po->get_layer(i)->print_z < bottom_z) + break; + for (const auto &link : current_links) { + const LayerSlice &slice_below = po->get_layer(i)->lslices_ex[link.slice_idx]; + next_links.insert(next_links.end(), slice_below.overlaps_below.begin(), slice_below.overlaps_below.end()); + std::unordered_set regions_under_to_check; + for (const LayerIsland &island : slice_below.islands) { + regions_under_to_check.insert(po->get_layer(i)->regions()[island.perimeters.region()]); + if (!island.fill_expolygons_composite()) { + regions_under_to_check.insert(po->get_layer(i)->regions()[island.fill_region_id]); + } else { + for (const auto &r : po->get_layer(i)->regions()) { + regions_under_to_check.insert(r); + } + break; + } + } + + for (const LayerRegion *region : regions_under_to_check) { + bool has_low_density = region->region().config().fill_density.value < 100; + bool has_special_infill = region_has_special_infill(region); + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type == stInternal && has_low_density && !has_special_infill) { + Polygons p = to_polygons(surface.expolygon); + lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); + } else if (surface.surface_type == stInternal && has_low_density && has_special_infill) { + Polygons p = to_polygons(surface.expolygon); + special_infill.insert(special_infill.end(), p.begin(), p.end()); + } else { + Polygons p = to_polygons(surface.expolygon); + not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); + } + } + } + } + current_links = next_links; + next_links.clear(); + } + + lower_layers_sparse_infill = intersection(lower_layers_sparse_infill, + layer->lslices[int(candidates.first - layer->lslices_ex.data())]); + lower_layers_sparse_infill = diff(lower_layers_sparse_infill, not_sparse_infill); + special_infill = intersection(special_infill, layer->lslices[int(candidates.first - layer->lslices_ex.data())]); + special_infill = diff(special_infill, not_sparse_infill); + + lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), special_infill.begin(), special_infill.end()); + + if (shrink(lower_layers_sparse_infill, 3.0 * scale_(max_bridge_flow_height[candidates.first])).empty()) { + continue; + } + }; + + tbb::parallel_for(tbb::blocked_range(0, jobs.size()), [po = this, &jobs, &surfaces_by_layer](tbb::blocked_range r) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + for (size_t lidx = jobs[job_idx].first; lidx < jobs[job_idx].second; lidx++) { + const Layer *layer = po->get_layer(lidx); + + // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that would otherwise + // compete over anchoring sparse infill lines, leaving one area unachored + std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(), [](const Surface* left, const Surface* right){ + auto a = get_extents(left->expolygon); + auto b = get_extents(right->expolygon); + + if (a.min.x() == b.min.x()) { + return a.min.y() < b.min.y(); + }; + return a.min.x() < b.min.x(); + }); + } + } + }); + std::unordered_map> bridging_surfaces; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &bridging_surfaces](tbb::blocked_range r) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { const Layer *layer = po->get_layer(lidx); From f8e7d1b01c114b4d45f9e221c6b5bb935065d650 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 9 Mar 2023 16:17:08 +0100 Subject: [PATCH 066/201] core implemented, now fixing the issues --- src/libslic3r/Fill/Fill.cpp | 6 +- src/libslic3r/PrintObject.cpp | 956 +++++++++++++--------------------- 2 files changed, 380 insertions(+), 582 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 8ca0199d0..a6b420607 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -16,6 +16,7 @@ #include "FillLightning.hpp" #include "FillConcentric.hpp" #include "FillEnsuring.hpp" +#include "Polygon.hpp" namespace Slic3r { @@ -649,7 +650,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc for (SurfaceFill &surface_fill : surface_fills) { switch (surface_fill.params.pattern) { - case ipLightning: continue; break; + case ipLightning: { + auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); + sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end()); + }; break; case ipCount: continue; break; case ipSupportBase: continue; break; case ipEnsuring: continue; break; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 33029aac5..42e652437 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -16,6 +16,7 @@ #include "Layer.hpp" #include "MutablePolygon.hpp" #include "PrintBase.hpp" +#include "PrintConfig.hpp" #include "SupportMaterial.hpp" #include "TreeSupport.hpp" #include "Surface.hpp" @@ -1648,669 +1649,462 @@ void PrintObject::bridge_over_infill() } tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), + &layers_to_generate_infill, &infill_lines](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), po->adaptive_fill_octrees.second.get()); } }); - std::vector> jobs; + // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another + std::vector> clustered_layers_for_threads; for (auto pair : surfaces_by_layer) { - if (jobs.empty() || jobs.back().second < pair.first) { - jobs.emplace_back(pair.first, pair.first + 1); + if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z > + this->get_layer(pair.first)->print_z - + this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - + EPSILON) { + clustered_layers_for_threads.push_back({pair.first}); } else { - jobs.back().second = pair.first + 1; + clustered_layers_for_threads.back().push_back(pair.first); } } - auto gahter_lower_layers_sparse_infill = [](const PrintObject *po, int lidx, float target_flow_height) { - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons special_infill{}; - Polygons not_sparse_infill{}; - double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; - for (int i = int(lidx) - 1; i >= 0; --i) { - // Stop iterating if layer is lower than bottom_z. - if (po->get_layer(i)->print_z < bottom_z) - break; - for (const auto &link : current_links) { - const LayerSlice &slice_below = po->get_layer(i)->lslices_ex[link.slice_idx]; - next_links.insert(next_links.end(), slice_below.overlaps_below.begin(), slice_below.overlaps_below.end()); - std::unordered_set regions_under_to_check; - for (const LayerIsland &island : slice_below.islands) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.fill_region_id]); - } else { - for (const auto &r : po->get_layer(i)->regions()) { - regions_under_to_check.insert(r); - } - break; - } - } + // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. + auto gather_areas_w_depth = + [](const PrintObject *po, int lidx, float target_flow_height) { + // Gather lower layers sparse infill areas, to depth defined by used bridge flow + Polygons lower_layers_sparse_infill{}; + Polygons not_sparse_infill{}; + double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; + for (int i = int(lidx) - 1; i >= 0; --i) { + // Stop iterating if layer is lower than bottom_z. + const Layer *layer = po->get_layer(i); + if (layer->print_z < bottom_z) + break; - for (const LayerRegion *region : regions_under_to_check) { - bool has_low_density = region->region().config().fill_density.value < 100; - bool has_special_infill = region_has_special_infill(region); + for (const LayerRegion *region : layer->regions()) { + bool has_low_density = region->region().config().fill_density.value < 100; for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density && !has_special_infill) { + if (surface.surface_type == stInternal && has_low_density) { Polygons p = to_polygons(surface.expolygon); lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); - } else if (surface.surface_type == stInternal && has_low_density && has_special_infill) { - Polygons p = to_polygons(surface.expolygon); - special_infill.insert(special_infill.end(), p.begin(), p.end()); } else { Polygons p = to_polygons(surface.expolygon); not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); } } } + lower_layers_sparse_infill = union_(lower_layers_sparse_infill); + } + + return diff(lower_layers_sparse_infill, not_sparse_infill); + }; + + // LAMBDA do determine optimal bridging angle + auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern) { + AABBTreeLines::LinesDistancer lines_tree(anchors); + + std::map counted_directions; + for (const Polygon &p : bridged_area) { + for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) { + Vec2d start = p.points[point_idx].cast(); + Vec2d next = p.points[point_idx + 1].cast(); + Vec2d v = next - start; // vector from next to current + double dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / scaled(3.0))); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Point a = (start + v * (i * step_size)).cast(); + auto [distance, index, p] = lines_tree.distance_from_lines_extra(a); + double angle = lines_tree.get_line(index).orientation(); + if (angle > PI) { + angle -= PI; + } + angle += PI * 0.5; + counted_directions[angle]++; + } } - current_links = next_links; - next_links.clear(); } - lower_layers_sparse_infill = intersection(lower_layers_sparse_infill, - layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - lower_layers_sparse_infill = diff(lower_layers_sparse_infill, not_sparse_infill); - special_infill = intersection(special_infill, layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - special_infill = diff(special_infill, not_sparse_infill); + std::pair best_dir{0, 0}; + // sliding window accumulation + for (const auto &dir : counted_directions) { + int score_acc = 0; + double dir_acc = 0; + double window_start_angle = dir.first - PI * 0.1; + double window_end_angle = dir.first + PI * 0.1; + for (auto dirs_window = counted_directions.lower_bound(window_start_angle); + dirs_window != counted_directions.upper_bound(window_end_angle); dirs_window++) { + dir_acc += dirs_window->first * dirs_window->second; + score_acc += dirs_window->second; + } + // current span of directions is 0.5 PI to 1.5 PI (due to the aproach.). Edge values should also account for the + // opposite direction. + if (window_start_angle < 0.5 * PI) { + for (auto dirs_window = counted_directions.lower_bound(1.5 * PI - (0.5 * PI - window_start_angle)); + dirs_window != counted_directions.end(); dirs_window++) { + dir_acc += dirs_window->first * dirs_window->second; + score_acc += dirs_window->second; + } + } + if (window_start_angle > 1.5 * PI) { + for (auto dirs_window = counted_directions.begin(); + dirs_window != counted_directions.upper_bound(window_start_angle - 1.5 * PI); dirs_window++) { + dir_acc += dirs_window->first * dirs_window->second; + score_acc += dirs_window->second; + } + } - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), special_infill.begin(), special_infill.end()); - - if (shrink(lower_layers_sparse_infill, 3.0 * scale_(max_bridge_flow_height[candidates.first])).empty()) { - continue; + if (score_acc > best_dir.second) { + best_dir = {dir_acc / score_acc, score_acc}; + } } + double bridging_angle = best_dir.first; + if (bridging_angle == 0) { + bridging_angle = 0.001; + } + switch (dominant_pattern) { + case ipHilbertCurve: bridging_angle += 0.25 * PI; break; + case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break; + default: break; + } + + return bridging_angle; }; - tbb::parallel_for(tbb::blocked_range(0, jobs.size()), [po = this, &jobs, &surfaces_by_layer](tbb::blocked_range r) { - for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { - for (size_t lidx = jobs[job_idx].first; lidx < jobs[job_idx].second; lidx++) { - const Layer *layer = po->get_layer(lidx); - - // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that would otherwise - // compete over anchoring sparse infill lines, leaving one area unachored - std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(), [](const Surface* left, const Surface* right){ - auto a = get_extents(left->expolygon); - auto b = get_extents(right->expolygon); - - if (a.min.x() == b.min.x()) { - return a.min.y() < b.min.y(); - }; - return a.min.x() < b.min.x(); - }); + // LAMBDA that will fill given polygons with lines, exapand the lines to the nearest anchor, and reconstruct polygons from the newly + // generated lines + auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle) { + auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) { + for (Line &l : lines) { + double ax = double(l.a.x()); + double ay = double(l.a.y()); + l.a.x() = coord_t(round(cos_angle * ax - sin_angle * ay)); + l.a.y() = coord_t(round(cos_angle * ay + sin_angle * ax)); + double bx = double(l.b.x()); + double by = double(l.b.y()); + l.b.x() = coord_t(round(cos_angle * bx - sin_angle * by)); + l.b.y() = coord_t(round(cos_angle * by + sin_angle * bx)); } - } - }); + }; - std::unordered_map> bridging_surfaces; + auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) { + return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) || + (bhigh >= alow && bhigh <= ahigh); + }; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, - &bridging_surfaces](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - const Layer *layer = po->get_layer(lidx); + Polygons expanded_bridged_area{}; + double aligning_angle = -bridging_angle + PI * 0.5; + { + polygons_rotate(bridged_area, aligning_angle); + lines_rotate(anchors, cos(aligning_angle), sin(aligning_angle)); + BoundingBox bb_x = get_extents(bridged_area); + BoundingBox bb_y = get_extents(anchors); - // gather also sparse infill surfaces on this layer, to which we can expand the bridges for anchoring - // gather potential internal bridging surfaces for the current layer - // pair of LayerSlice idx and surfaces. The LayerSlice idx simplifies the processing, since we cannot expand beyond it - std::unordered_map bridging_surface_candidates; - std::unordered_map expansion_space; - std::unordered_map max_bridge_flow_height; - std::unordered_map surface_to_region; - for (const LayerSlice &slice : layer->lslices_ex) { - AABBTreeLines::LinesDistancer slice_island_tree{to_lines(layer->lslices[int(&slice - layer->lslices_ex.data())])}; - std::unordered_set regions_to_check; + const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + bridging_flow.scaled_spacing() - 1) / bridging_flow.scaled_spacing(); + std::vector vertical_lines(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { + coord_t x = bb_x.min.x() + i * bridging_flow.scaled_spacing(); + coord_t y_min = bb_y.min.y() - bridging_flow.scaled_spacing(); + coord_t y_max = bb_y.max.y() + bridging_flow.scaled_spacing(); + vertical_lines[i].a = Point{x, y_min}; + vertical_lines[i].b = Point{x, y_max}; + } - // If there is composite island we have to check all regions on the layer. otherwise, only some regions are needed to be checked - for (const LayerIsland &island : slice.islands) { - regions_to_check.insert(layer->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_to_check.insert(layer->regions()[island.fill_region_id]); + auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors)}; + auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; + +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(lidx) + "sliced", to_lines(bridged_area), anchors_and_walls, vertical_lines, {}); +#endif + + std::vector> polygon_sections(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { + auto area_intersections = bridged_area_tree.intersections_with_line(vertical_lines[i]); + for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { + if (bridged_area_tree.outside( + (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) { + polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, + area_intersections[intersection_idx + 1].first); + } + } + auto anchors_intersections = anchors_and_walls_tree.intersections_with_line(vertical_lines[i]); + + for (Line §ion : polygon_sections[i]) { + auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a, + [](const Point &a, const std::pair &b) { + return a.y() > b.first.y(); + }); + if (maybe_below_anchor != anchors_intersections.rend()) { + section.a = maybe_below_anchor->first; + section.a.y() -= bridging_flow.scaled_width() * (0.5 + 1.0); + } + + auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b, + [](const Point &a, const std::pair &b) { + return a.y() < b.first.y(); + }); + if (maybe_upper_anchor != anchors_intersections.end()) { + section.b = maybe_upper_anchor->first; + section.b.y() += bridging_flow.scaled_width() * (0.5 + 1.0); + } + } + + for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) { + Line §ion_a = polygon_sections[i][section_idx]; + Line §ion_b = polygon_sections[i][section_idx + 1]; + if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) { + section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a; + section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b; + section_a.a = section_a.b; + } + } + + polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), + [](const Line &s) { return s.a == s.b; }), + polygon_sections[i].end()); + } + + // reconstruct polygon from polygon sections + struct TracedPoly + { + std::vector lows; + std::vector highs; + }; + + std::vector current_traced_polys; + for (const auto &polygon_slice : polygon_sections) { + std::unordered_set used_segments; + for (TracedPoly &traced_poly : current_traced_polys) { + auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + + if (maybe_first_overlap != polygon_slice.end() && // segment exists + segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), + maybe_first_overlap->b.y())) // segment is overlapping + { + // Overlapping segment. In that case, add it + // to the traced polygon and add segment to used segments + if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < + 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { + traced_poly.lows.push_back(maybe_first_overlap->a); + } else { + traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a); + } + + if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < + 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { + traced_poly.highs.push_back(maybe_first_overlap->b); + } else { + traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b); + } + used_segments.insert(&(*maybe_first_overlap)); } else { - for (const auto& r : layer->regions()) { - regions_to_check.insert(r); - } - break; + // Zero or multiple overlapping segments. Resolving this is nontrivial, + // so we just close this polygon and maybe open several new. This will hopefully happen much less often + traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); + traced_poly.lows.clear(); + traced_poly.highs.clear(); } } - for ( const LayerRegion *region : regions_to_check) { - SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); + current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), + [](const TracedPoly &tp) { return tp.lows.empty(); }), + current_traced_polys.end()); - // filter out surfaces not from this island... TODO sotre this info in the Z-Graph, so that this filtering is not needed - // NOTE: we are keeping even very small internal ensuring overhangs here. The aim is to later differentiate between expanding wall ensuring regions - // where briding them would be conterproductive, and small ensuring islands that expand into large ones, where bridging is quite necessary - region_internal_solids.erase(std::remove_if(region_internal_solids.begin(), region_internal_solids.end(), - [slice_island_tree](const Surface *s) { - if (slice_island_tree.outside(s->expolygon.contour.first_point()) > 0) { - return true; - } - return false; - }), - region_internal_solids.end()); - if (!region_internal_solids.empty()) { - max_bridge_flow_height[&slice] = std::max(max_bridge_flow_height[&slice], - region->bridging_flow(frSolidInfill, true).height()); + for (const auto &segment : polygon_slice) { + if (used_segments.find(&segment) == used_segments.end()) { + TracedPoly &new_tp = current_traced_polys.emplace_back(); + new_tp.lows.push_back(segment.a - Point{bridging_flow.scaled_spacing() / 2, 0}); + new_tp.lows.push_back(segment.a); + new_tp.highs.push_back(segment.b - Point{bridging_flow.scaled_spacing() / 2, 0}); + new_tp.highs.push_back(segment.b); } - for (const Surface *s : region_internal_solids) { - surface_to_region[s] = region; - } - bridging_surface_candidates[&slice].insert(bridging_surface_candidates[&slice].end(), region_internal_solids.begin(), - region_internal_solids.end()); - auto region_sparse_infill = region->fill_surfaces().filter_by_type(stInternal); - expansion_space[&slice].insert(expansion_space[&slice].end(), region_sparse_infill.begin(), region_sparse_infill.end()); } } - // if there are none briding candidates, exit now, before making infill for the previous layer - if (std::all_of(bridging_surface_candidates.begin(), bridging_surface_candidates.end(), - [](const std::pair &candidates) { return candidates.second.empty(); })) { - continue; + // add not closed polys + for (TracedPoly &traced_poly : current_traced_polys) { + Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); } - // generate sparse infill polylines from lower layers to get anchorable polylines - Polylines lower_layer_polylines = po->get_layer(lidx)->lower_layer - ? po->get_layer(lidx)->lower_layer->generate_sparse_infill_polylines_for_anchoring() - : Polylines(); +#ifdef DEBUG_BRIDGE_OVER_INFILL + Lines l{}; + for (const auto &s : polygon_sections) { + l.insert(l.end(), s.begin(), s.end()); + } + debug_draw(std::to_string(lidx) + "reconstructed", l, anchors_and_walls_tree.get_lines(), to_lines(expanded_bridged_area), + bridged_area_tree.get_lines()); +#endif + } - for (std::pair candidates : bridging_surface_candidates) { - if (candidates.second.empty()) { - continue; - }; + polygons_rotate(expanded_bridged_area, -aligning_angle); + return expanded_bridged_area; + }; - auto region_has_special_infill = [](const LayerRegion *layer_region) { - switch (layer_region->region().config().fill_pattern.value) { - case ipAdaptiveCubic: return true; - case ipSupportCubic: return true; - case ipLightning: return true; - default: return false; - } - }; + tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = this, &surfaces_by_layer, + &clustered_layers_for_threads, + &gather_areas_w_depth, + &infill_lines, + &determine_bridging_angle, + &construct_anchored_polygon] + (tbb::blocked_range r) { + for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { + for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { + size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; + const Layer *layer = po->get_layer(lidx); + // this thread has exclusive access to all surfaces in layers enumerated in + // clustered_layers_for_threads[cluster_idx] - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons special_infill{}; - Polygons not_sparse_infill{}; - { - double bottom_z = layer->print_z - max_bridge_flow_height[candidates.first] - EPSILON; - std::vector current_links{}; - current_links.insert(current_links.end(), candidates.first->overlaps_below.begin(), - candidates.first->overlaps_below.end()); - std::vector next_links{}; - for (int i = int(lidx) - 1; i >= 0; --i) { - // Stop iterating if layer is lower than bottom_z. - if (po->get_layer(i)->print_z < bottom_z) + // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that + // would otherwise compete over anchoring sparse infill lines, leaving one area unachored + std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(), + [](const CandidateSurface &left, const CandidateSurface &right) { + auto a = get_extents(left.new_polys); + auto b = get_extents(right.new_polys); + + if (a.min.x() == b.min.x()) { + return a.min.y() < b.min.y(); + }; + return a.min.x() < b.min.x(); + }); + + // Gather deep infill areas, where thick bridges fit + coordf_t thick_bridges_depth = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height(); + Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth); + + // Now also remove area that has been already filled on lower layers by bridging expansion - For this + // reason we did the clustering of layers per thread. + double bottom_z = po->get_layer(lidx)->print_z - thick_bridges_depth - EPSILON; + if (job_idx > 0) { + for (int lower_job_idx = job_idx; lower_job_idx >= 0; lower_job_idx--) { + size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; + const Layer *lower_layer = po->get_layer(lower_layer_idx); + if (lower_layer->print_z >= bottom_z) { + for (const auto &c : surfaces_by_layer[lower_layer_idx]) { + deep_infill_area = diff(deep_infill_area, c.new_polys); + } + } else { break; - for (const auto &link : current_links) { - const LayerSlice &slice_below = po->get_layer(i)->lslices_ex[link.slice_idx]; - next_links.insert(next_links.end(), slice_below.overlaps_below.begin(), slice_below.overlaps_below.end()); - std::unordered_set regions_under_to_check; - for (const LayerIsland &island : slice_below.islands) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.fill_region_id]); - } else { - for (const auto &r : po->get_layer(i)->regions()) { - regions_under_to_check.insert(r); - } - break; - } - } - - for (const LayerRegion *region : regions_under_to_check) { - bool has_low_density = region->region().config().fill_density.value < 100; - bool has_special_infill = region_has_special_infill(region); - for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density && !has_special_infill) { - Polygons p = to_polygons(surface.expolygon); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); - } else if (surface.surface_type == stInternal && has_low_density && has_special_infill) { - Polygons p = to_polygons(surface.expolygon); - special_infill.insert(special_infill.end(), p.begin(), p.end()); - } else { - Polygons p = to_polygons(surface.expolygon); - not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); - } - } - } } - current_links = next_links; - next_links.clear(); } - lower_layers_sparse_infill = intersection(lower_layers_sparse_infill, - layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - lower_layers_sparse_infill = diff(lower_layers_sparse_infill, not_sparse_infill); - special_infill = intersection(special_infill, layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - special_infill = diff(special_infill, not_sparse_infill); + // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors + Polygons expansion_area; + for (const LayerRegion *region : layer->regions()) { + auto polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); + expansion_area.insert(expansion_area.end(), polys.begin(), polys.end()); + } + expansion_area = closing(expansion_area, SCALED_EPSILON); + expansion_area = intersection(expansion_area, deep_infill_area); + Lines anchors = to_lines(intersection_pl(infill_lines[lidx - 1], expansion_area)); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), special_infill.begin(), special_infill.end()); + std::vector expanded_surfaces; + expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); + for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { + const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); + Polygons area_to_be_bridged = intersection(candidate.new_polys, deep_infill_area); - if (shrink(lower_layers_sparse_infill, 3.0 * scale_(max_bridge_flow_height[candidates.first])).empty()) { + if (area_to_be_bridged.empty()) continue; - } - } - if (expansion_space[candidates.first].empty() && special_infill.empty()) { - // there is no expansion space to which can anchors expand on this island, add back original polygons and skip the island - for (const Surface *candidate : candidates.second) { - bridging_surfaces[candidates.first].emplace_back(candidate, to_polygons(candidate->expolygon), - surface_to_region[candidate], 0); - } - continue; - } - - Polygons expand_area; - for (const Surface *sparse_infill : expansion_space[candidates.first]) { - assert(sparse_infill->surface_type == stInternal); - Polygons a = to_polygons(sparse_infill->expolygon); - expand_area.insert(expand_area.end(), a.begin(), a.end()); - } - - // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that would otherwise - // compete over anchoring sparse infill lines, leaving one area unachored - std::sort(candidates.second.begin(), candidates.second.end(), [](const Surface* left, const Surface* right){ - auto a = get_extents(left->expolygon); - auto b = get_extents(right->expolygon); - - if (a.min.x() == b.min.x()) { - return a.min.y() < b.min.y(); - }; - return a.min.x() < b.min.x(); - }); - - std::unordered_map> infill_and_deep_infill_polygons_per_region; - for (const auto &surface_region : surface_to_region) { - const LayerRegion *r = surface_region.second; - if (infill_and_deep_infill_polygons_per_region.find(r) == infill_and_deep_infill_polygons_per_region.end()) { - const Flow &flow = r->bridging_flow(frSolidInfill, true); - Polygons infill_region = to_polygons(r->fill_expolygons()); - Polygons deep_infill_area = closing(infill_region, scale_(0.01), scale_(0.01) + 4.0 * flow.scaled_spacing()); - Polygons solid_supported_area = expand(not_sparse_infill, 4.0 * flow.scaled_spacing()); - infill_and_deep_infill_polygons_per_region[r] = {closing(infill_region, float(scale_(0.1))), - intersection(lower_layers_sparse_infill, - diff(deep_infill_area, solid_supported_area))}; - } - } - - // Lower layers sparse infill sections gathered - // now we can intersected them with bridging surface candidates to get actual areas that need and can accumulate - // bridging. These areas we then expand (within the surrounding sparse infill only!) - // to touch the infill polylines on previous layer. - for (const Surface *candidate : candidates.second) { - const Flow &flow = surface_to_region[candidate]->bridging_flow(frSolidInfill, true); - assert(candidate->surface_type == stInternalSolid); - - Polygons bridged_area = intersection(expand(to_polygons(candidate->expolygon), flow.scaled_spacing()), - infill_and_deep_infill_polygons_per_region[surface_to_region[candidate]].first); - // cut off parts which are not over sparse infill - material overflow - Polygons worth_bridging = intersection(bridged_area, - infill_and_deep_infill_polygons_per_region[surface_to_region[candidate]].second); - if (worth_bridging.empty()) { + Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridged, flow.scaled_spacing())); + Lines boundary_lines = to_lines(boundary_area); + if (boundary_lines.empty()) continue; - } - bridged_area = intersection(bridged_area, expand(worth_bridging, 5.0 * flow.scaled_spacing())); - Polygons max_area = expand_area; - max_area.insert(max_area.end(), bridged_area.begin(), bridged_area.end()); - max_area = closing(max_area, flow.scaled_spacing()); - - Polylines anchors = intersection_pl(lower_layer_polylines, max_area); - if (!special_infill.empty()) { - auto part_over_special_infill = intersection(special_infill, bridged_area); - auto artificial_boundary = to_polylines(expand(part_over_special_infill, 0.5 * flow.scaled_width())); - anchors.insert(anchors.end(), artificial_boundary.begin(), artificial_boundary.end()); - -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "special", to_lines(part_over_special_infill), to_lines(artificial_boundary), - to_lines(anchors), to_lines(expand_area)); -#endif - } - anchors = diff_pl(anchors, bridged_area); - - Lines anchors_and_walls = to_lines(anchors); - Lines tmp = to_lines(max_area); - anchors_and_walls.insert(anchors_and_walls.end(), tmp.begin(), tmp.end()); - -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "candidate", to_lines(candidate->expolygon), to_lines(bridged_area), - to_lines(max_area), (anchors_and_walls)); -#endif - - double bridging_angle = 0; - Polygons tmp_expanded_area = expand(bridged_area, 3.0 * flow.scaled_spacing()); - for (const ModifiedSurface& s : bridging_surfaces[candidates.first]) { + double bridging_angle = 0; + Polygons tmp_expanded_area = expand(area_to_be_bridged, 3.0 * flow.scaled_spacing()); + for (const CandidateSurface &s : expanded_surfaces) { if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; break; } } if (bridging_angle == 0) { - AABBTreeLines::LinesDistancer lines_tree{anchors.empty() ? anchors_and_walls : to_lines(anchors)}; - - std::map counted_directions; - for (const Polygon &p : bridged_area) { - for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) { - Vec2d start = p.points[point_idx].cast(); - Vec2d next = p.points[point_idx + 1].cast(); - Vec2d v = next - start; // vector from next to current - double dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / scaled(3.0))); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Point a = (start + v * (i * step_size)).cast(); - auto [distance, index, p] = lines_tree.distance_from_lines_extra(a); - double angle = lines_tree.get_line(index).orientation(); - if (angle > PI) { - angle -= PI; - } - angle += PI * 0.5; - counted_directions[angle]++; - } - } - } - - std::pair best_dir{0, 0}; - // sliding window accumulation - for (const auto &dir : counted_directions) { - int score_acc = 0; - double dir_acc = 0; - double window_start_angle = dir.first - PI * 0.1; - double window_end_angle = dir.first + PI * 0.1; - for (auto dirs_window = counted_directions.lower_bound(window_start_angle); - dirs_window != counted_directions.upper_bound(window_end_angle); dirs_window++) { - dir_acc += dirs_window->first * dirs_window->second; - score_acc += dirs_window->second; - } - // current span of directions is 0.5 PI to 1.5 PI (due to the aproach.). Edge values should also account for the - // opposite direction. - if (window_start_angle < 0.5 * PI) { - for (auto dirs_window = counted_directions.lower_bound(1.5 * PI - (0.5 * PI - window_start_angle)); - dirs_window != counted_directions.end(); dirs_window++) { - dir_acc += dirs_window->first * dirs_window->second; - score_acc += dirs_window->second; - } - } - if (window_start_angle > 1.5 * PI) { - for (auto dirs_window = counted_directions.begin(); - dirs_window != counted_directions.upper_bound(window_start_angle - 1.5 * PI); dirs_window++) { - dir_acc += dirs_window->first * dirs_window->second; - score_acc += dirs_window->second; - } - } - - if (score_acc > best_dir.second) { - best_dir = {dir_acc / score_acc, score_acc}; - } - } - bridging_angle = best_dir.first; - if (bridging_angle == 0) { - bridging_angle = 0.001; - } - switch (surface_to_region[candidate]->region().config().fill_pattern.value) { - case ipHilbertCurve: bridging_angle += 0.25 * PI; break; - case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break; - default: break; + if (!anchors.empty()) { + bridging_angle = determine_bridging_angle(area_to_be_bridged, anchors, + candidate.region->region().config().fill_pattern.value); + } else { + // use expansion boundaries as anchors. However the current area must be removed from such filter. + // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. + bridging_angle = determine_bridging_angle(area_to_be_bridged, boundary_lines, InfillPattern::ipLine); } } - auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) { - for (Line &l : lines) { - double ax = double(l.a.x()); - double ay = double(l.a.y()); - l.a.x() = coord_t(round(cos_angle * ax - sin_angle * ay)); - l.a.y() = coord_t(round(cos_angle * ay + sin_angle * ax)); - double bx = double(l.b.x()); - double by = double(l.b.y()); - l.b.x() = coord_t(round(cos_angle * bx - sin_angle * by)); - l.b.y() = coord_t(round(cos_angle * by + sin_angle * bx)); - } - }; + boundary_lines.insert(boundary_lines.end(), anchors.begin(), anchors.end()); + Polygons bridged_area = construct_anchored_polygon(area_to_be_bridged, boundary_lines, flow, bridging_angle); + bridged_area = intersection(bridged_area, boundary_area); + bridged_area = opening(bridged_area, flow.scaled_spacing()); + expansion_area = diff(expansion_area, bridged_area); - auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) { - return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) || - (bhigh >= alow && bhigh <= ahigh); - }; - - Polygons expanded_bridged_area{}; - double aligning_angle = -bridging_angle + PI * 0.5; - { - polygons_rotate(bridged_area, aligning_angle); - lines_rotate(anchors_and_walls, cos(aligning_angle), sin(aligning_angle)); - BoundingBox bb_x = get_extents(bridged_area); - BoundingBox bb_y = get_extents(anchors_and_walls); - - const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + flow.scaled_spacing() - 1) / flow.scaled_spacing(); - std::vector vertical_lines(n_vlines); - for (size_t i = 0; i < n_vlines; i++) { - coord_t x = bb_x.min.x() + i * flow.scaled_spacing(); - coord_t y_min = bb_y.min.y() - flow.scaled_spacing(); - coord_t y_max = bb_y.max.y() + flow.scaled_spacing(); - vertical_lines[i].a = Point{x, y_min}; - vertical_lines[i].b = Point{x, y_max}; - } - - auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors_and_walls)}; - auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; - -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "sliced", to_lines(bridged_area), anchors_and_walls, - vertical_lines, {}); -#endif - - std::vector> polygon_sections(n_vlines); - for (size_t i = 0; i < n_vlines; i++) { - auto area_intersections = bridged_area_tree.intersections_with_line(vertical_lines[i]); - for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { - if (bridged_area_tree.outside( - (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) { - polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, - area_intersections[intersection_idx + 1].first); - } - } - auto anchors_intersections = anchors_and_walls_tree.intersections_with_line(vertical_lines[i]); - - for (Line §ion : polygon_sections[i]) { - auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), - section.a, - [](const Point &a, const std::pair &b) { - return a.y() > b.first.y(); - }); - if (maybe_below_anchor != anchors_intersections.rend()) { - section.a = maybe_below_anchor->first; - section.a.y() -= flow.scaled_width() * (0.5 + 1.0); - } - - auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), - section.b, - [](const Point &a, const std::pair &b) { - return a.y() < b.first.y(); - }); - if (maybe_upper_anchor != anchors_intersections.end()) { - section.b = maybe_upper_anchor->first; - section.b.y() += flow.scaled_width() * (0.5 + 1.0); - } - } - - for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) { - Line §ion_a = polygon_sections[i][section_idx]; - Line §ion_b = polygon_sections[i][section_idx + 1]; - if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) { - section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a; - section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b; - section_a.a = section_a.b; - } - } - - polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), - [](const Line &s) { return s.a == s.b; }), - polygon_sections[i].end()); - } - - // reconstruct polygon from polygon sections - struct TracedPoly - { - std::vector lows; - std::vector highs; - }; - - std::vector current_traced_polys; - for (const auto &polygon_slice : polygon_sections) { - std::unordered_set used_segments; - for (TracedPoly &traced_poly : current_traced_polys) { - auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), - traced_poly.lows.back(), [](const Point &low, const Line &seg) { - return seg.b.y() > low.y(); - }); - - if (maybe_first_overlap != polygon_slice.end() && // segment exists - segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), - maybe_first_overlap->b.y())) // segment is overlapping - { - // Overlapping segment. In that case, add it - // to the traced polygon and add segment to used segments - if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < - 36.0 * double(flow.scaled_spacing()) * flow.scaled_spacing()) { - traced_poly.lows.push_back(maybe_first_overlap->a); - } else { - traced_poly.lows.push_back(traced_poly.lows.back() + Point{flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a - Point{flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a); - } - - if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < - 36.0 * double(flow.scaled_spacing()) * flow.scaled_spacing()) { - traced_poly.highs.push_back(maybe_first_overlap->b); - } else { - traced_poly.highs.push_back(traced_poly.highs.back() + Point{flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b - Point{flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b); - } - used_segments.insert(&(*maybe_first_overlap)); - } else { - // Zero or multiple overlapping segments. Resolving this is nontrivial, - // so we just close this polygon and maybe open several new. This will hopefully happen much less often - traced_poly.lows.push_back(traced_poly.lows.back() + Point{flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(traced_poly.highs.back() + Point{flow.scaled_spacing() / 2, 0}); - Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); - new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); - traced_poly.lows.clear(); - traced_poly.highs.clear(); - } - } - - current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), - [](const TracedPoly &tp) { return tp.lows.empty(); }), - current_traced_polys.end()); - - for (const auto &segment : polygon_slice) { - if (used_segments.find(&segment) == used_segments.end()) { - TracedPoly &new_tp = current_traced_polys.emplace_back(); - new_tp.lows.push_back(segment.a - Point{flow.scaled_spacing() / 2, 0}); - new_tp.lows.push_back(segment.a); - new_tp.highs.push_back(segment.b - Point{flow.scaled_spacing() / 2, 0}); - new_tp.highs.push_back(segment.b); - } - } - } - - // add not closed polys - for (TracedPoly &traced_poly : current_traced_polys) { - Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); - new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); - } - -#ifdef DEBUG_BRIDGE_OVER_INFILL - Lines l{}; - for (const auto &s : polygon_sections) { - l.insert(l.end(), s.begin(), s.end()); - } - debug_draw(std::to_string(lidx) + "reconstructed", l, anchors_and_walls_tree.get_lines(), - to_lines(expanded_bridged_area), bridged_area_tree.get_lines()); -#endif - } - - polygons_rotate(expanded_bridged_area, -aligning_angle); - expanded_bridged_area = intersection(expanded_bridged_area, max_area); - expanded_bridged_area = opening(expanded_bridged_area, flow.scaled_spacing()); - expand_area = diff(expand_area, expanded_bridged_area); - - bridging_surfaces[candidates.first].emplace_back(candidate, expanded_bridged_area, surface_to_region[candidate], - bridging_angle); -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "cadidate_added", to_lines(expanded_bridged_area), to_lines(bridged_area), - to_lines(max_area), to_lines(expand_area)); -#endif + expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); } + surfaces_by_layer[lidx].swap(expanded_surfaces); + expanded_surfaces.clear(); } } - }); + }); BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, - &bridging_surfaces](tbb::blocked_range r) { + &surfaces_by_layer](tbb::blocked_range r) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) + continue; Layer *layer = po->get_layer(lidx); - std::unordered_map new_surfaces; - for (const LayerSlice &slice : layer->lslices_ex) { - if (const auto &modified_surfaces = bridging_surfaces.find(&slice); - modified_surfaces != bridging_surfaces.end()) { - std::unordered_set regions_to_check; - for (const LayerIsland &island : slice.islands) { - regions_to_check.insert(layer->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_to_check.insert(layer->regions()[island.fill_region_id]); - } else { - for (LayerRegion *r : layer->regions()) { - regions_to_check.insert(r); - } - break; - } - } - - Polygons cut_from_infill{}; - for (const auto &surface : modified_surfaces->second) { - cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); - } - - for (const LayerRegion *region : regions_to_check) { - for (const ModifiedSurface &s : modified_surfaces->second) { - for (const Surface &surface : region->m_fill_surfaces.surfaces) { - if (s.original_surface == &surface) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, s.new_polys)) { - if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { - new_surfaces[region].emplace_back(tmp, expoly); - } - } - tmp.surface_type = stInternalBridge; - tmp.bridge_angle = s.bridge_angle; - for (const ExPolygon &expoly : union_ex(s.new_polys)) { - new_surfaces[region].emplace_back(tmp, expoly); - } - } else if (surface.surface_type == stInternal) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { - new_surfaces[region].emplace_back(tmp, expoly); - } - } else { - new_surfaces[region].push_back(surface); - } - } - } - } - } + Polygons cut_from_infill{}; + for (const auto &surface : surfaces_by_layer.at(lidx)) { + cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); } for (LayerRegion *region : layer->regions()) { - if (new_surfaces.find(region) != new_surfaces.end()) { - region->m_fill_surfaces = new_surfaces[region]; + Surfaces new_surfaces; + + for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { + for (Surface &surface : region->m_fill_surfaces.surfaces) { + if (cs.original_surface == &surface) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { + if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { + new_surfaces.emplace_back(tmp, expoly); + } + } + tmp.surface_type = stInternalBridge; + tmp.bridge_angle = cs.bridge_angle; + for (const ExPolygon &expoly : union_ex(cs.new_polys)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); + } else if (surface.surface_type == stInternal) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); + } + } } + region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), + new_surfaces.end()); + region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), + region->m_fill_surfaces.surfaces.end(), + [](const Surface &s) { return s.empty(); }), + region->m_fill_surfaces.surfaces.end()); } } }); From 3782d24ccfbf027ccf910d4c7c029eeb2d252c63 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 9 Mar 2023 17:04:35 +0100 Subject: [PATCH 067/201] Fixed bugs with bridging area determination --- src/libslic3r/PrintObject.cpp | 113 +++++++++++++++++----------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 42e652437..ad2f9da3c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1604,26 +1604,28 @@ void PrintObject::bridge_over_infill() continue; } auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); - Polygons internal_area = shrink(to_polygons(layer->lower_layer->lslices), 4 * spacing); + ExPolygons internal_area; Polygons lower_layer_solids; for (const LayerRegion *region : layer->lower_layer->regions()) { - bool has_low_density = region->region().config().fill_density.value < 100; + internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density) { + if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); } } } - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - internal_area = diff(internal_area, lower_layer_solids); + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + Polygons unsupported_area = to_polygons(internal_area); + unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { - Polygons away_from_perimeter = intersection(to_polygons(s->expolygon), internal_area); - if (!away_from_perimeter.empty()) { - Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(away_from_perimeter, 5 * spacing)); + Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + if (!unsupported.empty()) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); } } @@ -1956,11 +1958,10 @@ void PrintObject::bridge_over_infill() tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = this, &surfaces_by_layer, &clustered_layers_for_threads, - &gather_areas_w_depth, - &infill_lines, + &gather_areas_w_depth, &infill_lines, &determine_bridging_angle, - &construct_anchored_polygon] - (tbb::blocked_range r) { + &construct_anchored_polygon]( + tbb::blocked_range r) { for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; @@ -2000,6 +2001,7 @@ void PrintObject::bridge_over_infill() break; } } + } // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors Polygons expansion_area; @@ -2046,11 +2048,12 @@ void PrintObject::bridge_over_infill() boundary_lines.insert(boundary_lines.end(), anchors.begin(), anchors.end()); Polygons bridged_area = construct_anchored_polygon(area_to_be_bridged, boundary_lines, flow, bridging_angle); - bridged_area = intersection(bridged_area, boundary_area); - bridged_area = opening(bridged_area, flow.scaled_spacing()); - expansion_area = diff(expansion_area, bridged_area); + bridged_area = intersection(bridged_area, boundary_area); + bridged_area = opening(bridged_area, flow.scaled_spacing()); + expansion_area = diff(expansion_area, bridged_area); - expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); + expanded_surfaces.push_back( + CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); } surfaces_by_layer[lidx].swap(expanded_surfaces); expanded_surfaces.clear(); @@ -2058,58 +2061,56 @@ void PrintObject::bridge_over_infill() } }); - BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); + BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, - &surfaces_by_layer](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) - continue; - Layer *layer = po->get_layer(lidx); + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) + continue; + Layer *layer = po->get_layer(lidx); - Polygons cut_from_infill{}; - for (const auto &surface : surfaces_by_layer.at(lidx)) { - cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); - } + Polygons cut_from_infill{}; + for (const auto &surface : surfaces_by_layer.at(lidx)) { + cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); + } - for (LayerRegion *region : layer->regions()) { - Surfaces new_surfaces; + for (LayerRegion *region : layer->regions()) { + Surfaces new_surfaces; - for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { - for (Surface &surface : region->m_fill_surfaces.surfaces) { - if (cs.original_surface == &surface) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { - if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { - new_surfaces.emplace_back(tmp, expoly); - } - } - tmp.surface_type = stInternalBridge; - tmp.bridge_angle = cs.bridge_angle; - for (const ExPolygon &expoly : union_ex(cs.new_polys)) { + for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { + for (Surface &surface : region->m_fill_surfaces.surfaces) { + if (cs.original_surface == &surface) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { + if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { new_surfaces.emplace_back(tmp, expoly); } - surface.clear(); - } else if (surface.surface_type == stInternal) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { - new_surfaces.emplace_back(tmp, expoly); - } - surface.clear(); } + tmp.surface_type = stInternalBridge; + tmp.bridge_angle = cs.bridge_angle; + for (const ExPolygon &expoly : union_ex(cs.new_polys)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); + } else if (surface.surface_type == stInternal) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); } } - region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), - new_surfaces.end()); - region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), - region->m_fill_surfaces.surfaces.end(), - [](const Surface &s) { return s.empty(); }), - region->m_fill_surfaces.surfaces.end()); } + region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), new_surfaces.end()); + region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), + region->m_fill_surfaces.surfaces.end(), + [](const Surface &s) { return s.empty(); }), + region->m_fill_surfaces.surfaces.end()); } - }); + } + }); - BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info(); + BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info(); } // void PrintObject::bridge_over_infill() From ad693532d3167f8aca5103e9fd824a79ffdaf393 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 10 Mar 2023 13:13:40 +0100 Subject: [PATCH 068/201] Fixed clustering for threads issue - inverse comparison, lower job idx incorrectly initialized --- src/libslic3r/PrintObject.cpp | 55 +++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ad2f9da3c..4ee24235d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -1633,6 +1634,13 @@ void PrintObject::bridge_over_infill() } }); +#ifdef DEBUG_BRIDGE_OVER_INFILL + for (const auto &c : candidate_surfaces) { + debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), + to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); + } +#endif + std::map> surfaces_by_layer; std::vector> surfaces_w_bottom_z; for (const CandidateSurface& c : candidate_surfaces) { @@ -1661,10 +1669,17 @@ void PrintObject::bridge_over_infill() } }); +#ifdef DEBUG_BRIDGE_OVER_INFILL + for (const auto &il : infill_lines) { + debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {}); + } +#endif + // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; + // note: surfaces_by_layer is ordered map for (auto pair : surfaces_by_layer) { - if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z > + if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z < this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - EPSILON) { @@ -1674,6 +1689,17 @@ void PrintObject::bridge_over_infill() } } +#ifdef DEBUG_BRIDGE_OVER_INFILL + std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl; + for (auto cluster : clustered_layers_for_threads) { + std::cout << "CLUSTER: "; + for (auto l : cluster) { + std::cout << l << " "; + } + std::cout << std::endl; + } +#endif + // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. auto gather_areas_w_depth = [](const PrintObject *po, int lidx, float target_flow_height) { @@ -1820,10 +1846,6 @@ void PrintObject::bridge_over_infill() auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors)}; auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "sliced", to_lines(bridged_area), anchors_and_walls, vertical_lines, {}); -#endif - std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { auto area_intersections = bridged_area_tree.intersections_with_line(vertical_lines[i]); @@ -1941,15 +1963,6 @@ void PrintObject::bridge_over_infill() Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); } - -#ifdef DEBUG_BRIDGE_OVER_INFILL - Lines l{}; - for (const auto &s : polygon_sections) { - l.insert(l.end(), s.begin(), s.end()); - } - debug_draw(std::to_string(lidx) + "reconstructed", l, anchors_and_walls_tree.get_lines(), to_lines(expanded_bridged_area), - bridged_area_tree.get_lines()); -#endif } polygons_rotate(expanded_bridged_area, -aligning_angle); @@ -1990,7 +2003,7 @@ void PrintObject::bridge_over_infill() // reason we did the clustering of layers per thread. double bottom_z = po->get_layer(lidx)->print_z - thick_bridges_depth - EPSILON; if (job_idx > 0) { - for (int lower_job_idx = job_idx; lower_job_idx >= 0; lower_job_idx--) { + for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; const Layer *lower_layer = po->get_layer(lower_layer_idx); if (lower_layer->print_z >= bottom_z) { @@ -2024,9 +2037,6 @@ void PrintObject::bridge_over_infill() Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridged, flow.scaled_spacing())); Lines boundary_lines = to_lines(boundary_area); - if (boundary_lines.empty()) - continue; - double bridging_angle = 0; Polygons tmp_expanded_area = expand(area_to_be_bridged, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { @@ -2040,7 +2050,7 @@ void PrintObject::bridge_over_infill() bridging_angle = determine_bridging_angle(area_to_be_bridged, anchors, candidate.region->region().config().fill_pattern.value); } else { - // use expansion boundaries as anchors. However the current area must be removed from such filter. + // use expansion boundaries as anchors. // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. bridging_angle = determine_bridging_angle(area_to_be_bridged, boundary_lines, InfillPattern::ipLine); } @@ -2052,6 +2062,13 @@ void PrintObject::bridge_over_infill() bridged_area = opening(bridged_area, flow.scaled_spacing()); expansion_area = diff(expansion_area, bridged_area); +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + + "_expanded_bridging", + to_lines(layer->lslices), to_lines(candidate.original_surface->expolygon), to_lines(candidate.new_polys), + to_lines(bridged_area)); +#endif + expanded_surfaces.push_back( CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); } From d223eef38d2d9b1f8608b5522526ce4b1fe0a4b6 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 10 Mar 2023 15:35:11 +0100 Subject: [PATCH 069/201] Do not generate other than sparse infill lines Split jobs if candidates bounding boxes do not overlap - otherwise it can become completely linearized and very slow Improve formatting --- src/libslic3r/Fill/Fill.cpp | 4 + src/libslic3r/PrintObject.cpp | 198 ++++++++++++++++++++-------------- 2 files changed, 121 insertions(+), 81 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a6b420607..8db49e559 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -649,6 +649,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc Polylines sparse_infill_polylines{}; for (SurfaceFill &surface_fill : surface_fills) { + if (surface_fill.surface.surface_type != stInternal) { + continue; + } + switch (surface_fill.params.pattern) { case ipLightning: { auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4ee24235d..8a1bacb0b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1595,110 +1595,146 @@ void PrintObject::bridge_over_infill() double bridge_angle; }; - tbb::concurrent_vector candidate_surfaces; + std::map> surfaces_by_layer; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), - &candidate_surfaces](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - const Layer *layer = po->get_layer(lidx); - if (layer->lower_layer == nullptr) { - continue; - } - auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); - ExPolygons internal_area; - Polygons lower_layer_solids; - for (const LayerRegion *region : layer->lower_layer->regions()) { - internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); - for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { - Polygons p = to_polygons(surface.expolygon); - lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); + // SECTION to gather and filter surfaces for expanding, and then cluster them by layer + { + tbb::concurrent_vector candidate_surfaces; + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), + &candidate_surfaces](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + const Layer *layer = po->get_layer(lidx); + if (layer->lower_layer == nullptr) { + continue; + } + auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); + ExPolygons internal_area; + Polygons lower_layer_solids; + for (const LayerRegion *region : layer->lower_layer->regions()) { + internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { + Polygons p = to_polygons(surface.expolygon); + lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); + } + } + } + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + Polygons unsupported_area = to_polygons(internal_area); + unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = diff(unsupported_area, lower_layer_solids); + + for (const LayerRegion *region : layer->regions()) { + SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); + for (const Surface *s : region_internal_solids) { + Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + if (!unsupported.empty()) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); + candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + } } } } - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - Polygons unsupported_area = to_polygons(internal_area); - unsupported_area = shrink(unsupported_area, 4 * spacing); - unsupported_area = diff(unsupported_area, lower_layer_solids); - - for (const LayerRegion *region : layer->regions()) { - SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); - for (const Surface *s : region_internal_solids) { - Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - if (!unsupported.empty()) { - Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); - } - } - } - } - }); + }); #ifdef DEBUG_BRIDGE_OVER_INFILL - for (const auto &c : candidate_surfaces) { - debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), - to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); - } + for (const auto &c : candidate_surfaces) { + debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), + to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); + } #endif - std::map> surfaces_by_layer; - std::vector> surfaces_w_bottom_z; - for (const CandidateSurface& c : candidate_surfaces) { - surfaces_by_layer[c.region->layer()->id()].push_back(c); - surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z()); + for (const CandidateSurface &c : candidate_surfaces) { + surfaces_by_layer[c.region->layer()->id()].push_back(c); + } } - this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); - std::map infill_lines; - std::vector layers_to_generate_infill; - for (const auto& pair : surfaces_by_layer) { - assert(pair.first > 0); - infill_lines[pair.first-1] = {}; - layers_to_generate_infill.push_back(pair.first-1); - } - - tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), - &layers_to_generate_infill, - &infill_lines](tbb::blocked_range r) { - for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { - size_t lidx = layers_to_generate_infill[job_idx]; - infill_lines.at( - lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), - po->adaptive_fill_octrees.second.get()); + // SECTION to generate infill polylines + { + std::vector> surfaces_w_bottom_z; + for (const auto &pair : surfaces_by_layer) { + for (const CandidateSurface &c : pair.second) { + surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z()); + } } - }); + this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + std::vector layers_to_generate_infill; + for (const auto &pair : surfaces_by_layer) { + assert(pair.first > 0); + infill_lines[pair.first - 1] = {}; + layers_to_generate_infill.push_back(pair.first - 1); + } + + tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), + &layers_to_generate_infill, + &infill_lines](tbb::blocked_range r) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + size_t lidx = layers_to_generate_infill[job_idx]; + infill_lines.at( + lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), + po->adaptive_fill_octrees.second.get()); + } + }); #ifdef DEBUG_BRIDGE_OVER_INFILL - for (const auto &il : infill_lines) { - debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {}); - } + for (const auto &il : infill_lines) { + debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {}); + } #endif + } // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; - // note: surfaces_by_layer is ordered map - for (auto pair : surfaces_by_layer) { - if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z < - this->get_layer(pair.first)->print_z - - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - - EPSILON) { - clustered_layers_for_threads.push_back({pair.first}); - } else { - clustered_layers_for_threads.back().push_back(pair.first); + { + std::vector layers_with_candidates; + std::map layer_area_covered_by_candidates; + for (const auto& pair : surfaces_by_layer) { + layers_with_candidates.push_back(pair.first); + layer_area_covered_by_candidates[pair.first] = {}; + } + + tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, + &layer_area_covered_by_candidates]( + tbb::blocked_range r) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + size_t lidx = layers_with_candidates[job_idx]; + for (const auto &candidate : surfaces_by_layer.at(lidx)) { + Polygon candiate_inflated_aabb = get_extents(candidate.new_polys) + .inflated(candidate.region->flow(frSolidInfill, true).scaled_spacing() * 5) + .polygon(); + layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx), + Polygons{candiate_inflated_aabb}); + } + } + }); + + // note: surfaces_by_layer is ordered map + for (auto pair : surfaces_by_layer) { + if (clustered_layers_for_threads.empty() || + this->get_layer(clustered_layers_for_threads.back().back())->print_z < + this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - + EPSILON || + intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()], + layer_area_covered_by_candidates[pair.first]) + .empty()) { + clustered_layers_for_threads.push_back({pair.first}); + } else { + clustered_layers_for_threads.back().push_back(pair.first); + } } - } #ifdef DEBUG_BRIDGE_OVER_INFILL - std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl; - for (auto cluster : clustered_layers_for_threads) { - std::cout << "CLUSTER: "; - for (auto l : cluster) { - std::cout << l << " "; + std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl; + for (auto cluster : clustered_layers_for_threads) { + std::cout << "CLUSTER: "; + for (auto l : cluster) { + std::cout << l << " "; + } + std::cout << std::endl; } - std::cout << std::endl; - } #endif + } // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. auto gather_areas_w_depth = From 76209d89ff16f5adb51d26f0aaa63fab84591388 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 10 Mar 2023 16:59:21 +0100 Subject: [PATCH 070/201] Fixed lighting infill crash. TODO filtering of small ensuring regions --- src/libslic3r/Fill/Fill.cpp | 1 + src/libslic3r/PrintObject.cpp | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 8db49e559..e42c4babb 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -657,6 +657,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc case ipLightning: { auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end()); + continue; }; break; case ipCount: continue; break; case ipSupportBase: continue; break; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8a1bacb0b..38e3d3400 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1560,7 +1560,7 @@ void PrintObject::discover_vertical_shells() } // for each region } // void PrintObject::discover_vertical_shells() -// #define DEBUG_BRIDGE_OVER_INFILL +#define DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL template void debug_draw(std::string name, const T& a, const T& b, const T& c, const T& d) { @@ -1608,10 +1608,11 @@ void PrintObject::bridge_over_infill() continue; } auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); - ExPolygons internal_area; + Polygons unsupported_area; Polygons lower_layer_solids; for (const LayerRegion *region : layer->lower_layer->regions()) { - internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); + Polygons fill_polys = to_polygons(region->fill_expolygons()); + unsupported_area = union_(unsupported_area, fill_polys); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); @@ -1619,31 +1620,32 @@ void PrintObject::bridge_over_infill() } } } - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - Polygons unsupported_area = to_polygons(internal_area); - unsupported_area = shrink(unsupported_area, 4 * spacing); - unsupported_area = diff(unsupported_area, lower_layer_solids); + + //TODO if region touches the extremes, then check for its area and filter. otherwise, keep even the smallest one + + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - if (!unsupported.empty()) { + if (area(unsupported) > spacing * spacing) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(region->layer()->id()) + "_candidate_surface_" + std::to_string(area(s->expolygon)), + to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging), + to_lines(unsupported_area)); +#endif } } } } }); -#ifdef DEBUG_BRIDGE_OVER_INFILL - for (const auto &c : candidate_surfaces) { - debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), - to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); - } -#endif - for (const CandidateSurface &c : candidate_surfaces) { surfaces_by_layer[c.region->layer()->id()].push_back(c); } From 487d9209edf0db1620d409a2a2dd614c215a8454 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 13 Mar 2023 13:02:08 +0100 Subject: [PATCH 071/201] Fix and improve region filtering --- src/libslic3r/PrintObject.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 38e3d3400..0ef6043e1 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1607,12 +1607,12 @@ void PrintObject::bridge_over_infill() if (layer->lower_layer == nullptr) { continue; } - auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); + auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); Polygons unsupported_area; Polygons lower_layer_solids; for (const LayerRegion *region : layer->lower_layer->regions()) { Polygons fill_polys = to_polygons(region->fill_expolygons()); - unsupported_area = union_(unsupported_area, fill_polys); + unsupported_area = union_(unsupported_area, expand(fill_polys, spacing)); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); @@ -1621,17 +1621,16 @@ void PrintObject::bridge_over_infill() } } - //TODO if region touches the extremes, then check for its area and filter. otherwise, keep even the smallest one - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = shrink(unsupported_area, 5 * spacing); unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { - Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - if (area(unsupported) > spacing * spacing) { + Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; + if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); From 12f1cd0bc06e21f3e5230a19f08d8f0bb57e01c1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 13 Mar 2023 16:08:32 +0100 Subject: [PATCH 072/201] added semi support for lightning infill --- src/libslic3r/Fill/Fill.cpp | 14 ++++--- src/libslic3r/Layer.hpp | 3 +- src/libslic3r/Print.hpp | 4 +- src/libslic3r/PrintObject.cpp | 72 ++++++++++++++++++++++------------- 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index e42c4babb..a6e5e1fb4 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -640,7 +640,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: #endif } -Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) const +Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const { std::vector surface_fills = group_fills(*this); const Slic3r::BoundingBox bbox = this->object()->bounding_box(); @@ -654,14 +654,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc } switch (surface_fill.params.pattern) { - case ipLightning: { - auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); - sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end()); - continue; - }; break; case ipCount: continue; break; case ipSupportBase: continue; break; case ipEnsuring: continue; break; + case ipLightning: case ipAdaptiveCubic: case ipSupportCubic: case ipRectilinear: @@ -692,6 +688,12 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc f->print_config = &this->object()->print()->config(); f->print_object_config = &this->object()->config(); + if (surface_fill.params.pattern == ipLightning) { + auto *lf = dynamic_cast(f.get()); + lf->generator = lightning_generator; + lf->num_raft_layers = this->object()->slicing_parameters().raft_layers(); + } + // calculate flow spacing for infill pattern generation double link_max_length = 0.; if (!surface_fill.params.bridge) { diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 0cba55d3c..b3d071c9d 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -372,7 +372,8 @@ public: FillAdaptive::Octree *support_fill_octree, FillLightning::Generator *lightning_generator); Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree, - FillAdaptive::Octree *support_fill_octree) const; + FillAdaptive::Octree *support_fill_octree, + FillLightning::Generator* lightning_generator) const; void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index df2bde469..9fbbe378a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -2,6 +2,7 @@ #define slic3r_Print_hpp_ #include "Fill/FillAdaptive.hpp" +#include "Fill/FillLightning.hpp" #include "PrintBase.hpp" #include "BoundingBox.hpp" @@ -413,7 +414,8 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - std::pair adaptive_fill_octrees; + std::pair m_adaptive_fill_octrees; + FillLightning::GeneratorPtr m_lightning_generator; }; struct WipeTowerData diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0ef6043e1..2bd19ba5a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -404,17 +404,16 @@ void PrintObject::infill() if (this->set_started(posInfill)) { m_print->set_status(45, L("making infill")); - const auto& adaptive_fill_octree = this->adaptive_fill_octrees.first; - const auto& support_fill_octree = this->adaptive_fill_octrees.second; - auto lightning_generator = this->prepare_lightning_infill_data(); + const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; + const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator](const tbb::blocked_range& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), lightning_generator.get()); + m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get()); } } ); @@ -1560,7 +1559,7 @@ void PrintObject::discover_vertical_shells() } // for each region } // void PrintObject::discover_vertical_shells() -#define DEBUG_BRIDGE_OVER_INFILL +// #define DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL template void debug_draw(std::string name, const T& a, const T& b, const T& c, const T& d) { @@ -1586,13 +1585,22 @@ void PrintObject::bridge_over_infill() struct CandidateSurface { - CandidateSurface(const Surface *original_surface, Polygons new_polys, const LayerRegion *region, double bridge_angle) - : original_surface(original_surface), new_polys(new_polys), region(region), bridge_angle(bridge_angle) + CandidateSurface(const Surface *original_surface, + Polygons new_polys, + const LayerRegion *region, + double bridge_angle, + bool supported_by_lightning) + : original_surface(original_surface) + , new_polys(new_polys) + , region(region) + , bridge_angle(bridge_angle) + , supported_by_lightning(supported_by_lightning) {} const Surface *original_surface; Polygons new_polys; const LayerRegion *region; double bridge_angle; + bool supported_by_lightning; }; std::map> surfaces_by_layer; @@ -1610,7 +1618,11 @@ void PrintObject::bridge_over_infill() auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); Polygons unsupported_area; Polygons lower_layer_solids; + bool contains_only_lightning = true; for (const LayerRegion *region : layer->lower_layer->regions()) { + if (region->region().config().fill_pattern.value != ipLightning) { + contains_only_lightning = false; + } Polygons fill_polys = to_polygons(region->fill_expolygons()); unsupported_area = union_(unsupported_area, expand(fill_polys, spacing)); for (const Surface &surface : region->fill_surfaces()) { @@ -1632,7 +1644,7 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(region->layer()->id()) + "_candidate_surface_" + std::to_string(area(s->expolygon)), @@ -1660,7 +1672,9 @@ void PrintObject::bridge_over_infill() } } - this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + this->m_lightning_generator = this->prepare_lightning_infill_data(); + std::vector layers_to_generate_infill; for (const auto &pair : surfaces_by_layer) { assert(pair.first > 0); @@ -1674,8 +1688,9 @@ void PrintObject::bridge_over_infill() for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( - lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), - po->adaptive_fill_octrees.second.get()); + lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->m_adaptive_fill_octrees.first.get(), + po->m_adaptive_fill_octrees.second.get(), + po->m_lightning_generator.get()); } }); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -2061,21 +2076,21 @@ void PrintObject::bridge_over_infill() } expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); - Lines anchors = to_lines(intersection_pl(infill_lines[lidx - 1], expansion_area)); + Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); std::vector expanded_surfaces; expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); - Polygons area_to_be_bridged = intersection(candidate.new_polys, deep_infill_area); + Polygons area_to_be_bridge = intersection(candidate.new_polys, deep_infill_area); - if (area_to_be_bridged.empty()) + if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridged, flow.scaled_spacing())); - Lines boundary_lines = to_lines(boundary_area); + Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridge, flow.scaled_spacing())); + Polylines boundary_plines = to_polylines(boundary_area); double bridging_angle = 0; - Polygons tmp_expanded_area = expand(area_to_be_bridged, 3.0 * flow.scaled_spacing()); + Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; @@ -2084,30 +2099,33 @@ void PrintObject::bridge_over_infill() } if (bridging_angle == 0) { if (!anchors.empty()) { - bridging_angle = determine_bridging_angle(area_to_be_bridged, anchors, + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), candidate.region->region().config().fill_pattern.value); } else { // use expansion boundaries as anchors. // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. - bridging_angle = determine_bridging_angle(area_to_be_bridged, boundary_lines, InfillPattern::ipLine); + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine); } } - boundary_lines.insert(boundary_lines.end(), anchors.begin(), anchors.end()); - Polygons bridged_area = construct_anchored_polygon(area_to_be_bridged, boundary_lines, flow, bridging_angle); - bridged_area = intersection(bridged_area, boundary_area); - bridged_area = opening(bridged_area, flow.scaled_spacing()); - expansion_area = diff(expansion_area, bridged_area); + boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end()); + if (candidate.supported_by_lightning) { + boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10))); + } + Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + bridging_area = intersection(bridging_area, boundary_area); + bridging_area = opening(bridging_area, flow.scaled_spacing()); + expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_expanded_bridging", to_lines(layer->lslices), to_lines(candidate.original_surface->expolygon), to_lines(candidate.new_polys), - to_lines(bridged_area)); + to_lines(bridging_area)); #endif expanded_surfaces.push_back( - CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); + CandidateSurface(candidate.original_surface, bridging_area, candidate.region, bridging_angle, candidate.supported_by_lightning)); } surfaces_by_layer[lidx].swap(expanded_surfaces); expanded_surfaces.clear(); From 822ea84e0d483e4a4d8c5bdbb82c7a89e35618e2 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 13 Mar 2023 17:08:41 +0100 Subject: [PATCH 073/201] Fix lambda having modify access - not needed --- src/libslic3r/PrintObject.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2bd19ba5a..68ddb7ba9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2021,11 +2021,11 @@ void PrintObject::bridge_over_infill() return expanded_bridged_area; }; - tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = this, &surfaces_by_layer, - &clustered_layers_for_threads, - &gather_areas_w_depth, &infill_lines, - &determine_bridging_angle, - &construct_anchored_polygon]( + tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = static_cast(this), + &surfaces_by_layer, &clustered_layers_for_threads, + gather_areas_w_depth, &infill_lines, + determine_bridging_angle, + construct_anchored_polygon]( tbb::blocked_range r) { for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { From 4af37ca4a52c812f2b4fa77f2833d2e02c43e44d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 10:43:31 +0100 Subject: [PATCH 074/201] fix bug - layer id is not same as layer idx, because of raft (super confusing) --- src/libslic3r/PrintObject.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 68ddb7ba9..5efad85b7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1586,17 +1586,20 @@ void PrintObject::bridge_over_infill() struct CandidateSurface { CandidateSurface(const Surface *original_surface, + int layer_index, Polygons new_polys, const LayerRegion *region, double bridge_angle, bool supported_by_lightning) : original_surface(original_surface) + , layer_index(layer_index) , new_polys(new_polys) , region(region) , bridge_angle(bridge_angle) , supported_by_lightning(supported_by_lightning) {} const Surface *original_surface; + int layer_index; Polygons new_polys; const LayerRegion *region; double bridge_angle; @@ -1644,10 +1647,10 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0, contains_only_lightning)); + candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(region->layer()->id()) + "_candidate_surface_" + std::to_string(area(s->expolygon)), + debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)), to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging), to_lines(unsupported_area)); #endif @@ -1658,7 +1661,7 @@ void PrintObject::bridge_over_infill() }); for (const CandidateSurface &c : candidate_surfaces) { - surfaces_by_layer[c.region->layer()->id()].push_back(c); + surfaces_by_layer[c.layer_index].push_back(c); } } @@ -2053,7 +2056,7 @@ void PrintObject::bridge_over_infill() // Now also remove area that has been already filled on lower layers by bridging expansion - For this // reason we did the clustering of layers per thread. - double bottom_z = po->get_layer(lidx)->print_z - thick_bridges_depth - EPSILON; + double bottom_z = layer->print_z - thick_bridges_depth - EPSILON; if (job_idx > 0) { for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; @@ -2124,8 +2127,8 @@ void PrintObject::bridge_over_infill() to_lines(bridging_area)); #endif - expanded_surfaces.push_back( - CandidateSurface(candidate.original_surface, bridging_area, candidate.region, bridging_angle, candidate.supported_by_lightning)); + expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area, + candidate.region, bridging_angle, candidate.supported_by_lightning)); } surfaces_by_layer[lidx].swap(expanded_surfaces); expanded_surfaces.clear(); From 257837c07176e416a72bd1b58ae37bda7170922d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 11:38:16 +0100 Subject: [PATCH 075/201] Fix one and zero perimeter case - no bridging was generated --- src/libslic3r/PrintObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5efad85b7..8dbf96b26 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2090,7 +2090,7 @@ void PrintObject::bridge_over_infill() if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridge, flow.scaled_spacing())); + Polygons boundary_area = union_(expansion_area, area_to_be_bridge); Polylines boundary_plines = to_polylines(boundary_area); double bridging_angle = 0; Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); @@ -2118,7 +2118,7 @@ void PrintObject::bridge_over_infill() Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); bridging_area = intersection(bridging_area, boundary_area); bridging_area = opening(bridging_area, flow.scaled_spacing()); - expansion_area = diff(expansion_area, bridging_area); + expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + From c6fd339a39b0b7279eadf44936f7c26098ccec7c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 12:12:18 +0100 Subject: [PATCH 076/201] remove cracks in boundary surface via closing - without it, the bridges were sometimes cut in half --- src/libslic3r/PrintObject.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8dbf96b26..b16dea179 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2090,10 +2090,11 @@ void PrintObject::bridge_over_infill() if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, area_to_be_bridge); - Polylines boundary_plines = to_polylines(boundary_area); - double bridging_angle = 0; - Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); + Polygons boundary_area = union_(expansion_area, area_to_be_bridge); + boundary_area = closing(boundary_area, 0.3 * flow.scaled_spacing()); + Polylines boundary_plines = to_polylines(boundary_area); + double bridging_angle = 0; + Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; From eb73bf7f24b94c3b0b273026444c51f2b96078d9 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 16:40:55 +0100 Subject: [PATCH 077/201] Bridging over sparse infill - improve coliding regions merging, smoothen results, dissolve tiny ensuring regions around bridging --- src/libslic3r/Print.cpp | 2 +- src/libslic3r/PrintObject.cpp | 110 +++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a3eba0148..370fa3ba2 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1185,7 +1185,7 @@ void Print::alert_when_supports_needed() case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("thin fragile section"); break; } - return (critical ? "!" : "") + message; + return message; }; // vector of pairs of object and its issues, where each issue is a pair of type and critical flag diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b16dea179..ccdc7a914 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1647,6 +1647,12 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); + for (Polygon p : diff(to_polygons(s->expolygon), worth_bridging)) { + if (p.area() < region->flow(frSolidInfill, true).scaled_spacing() * scale_(12.0)) { + worth_bridging.push_back(p); + } + } + worth_bridging = intersection(closing(worth_bridging, 3 * region->flow(frSolidInfill, true).scaled_spacing()), s->expolygon); candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -1705,6 +1711,7 @@ void PrintObject::bridge_over_infill() // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; + float target_flow_height_factor = 0.5; { std::vector layers_with_candidates; std::map layer_area_covered_by_candidates; @@ -1732,7 +1739,8 @@ void PrintObject::bridge_over_infill() for (auto pair : surfaces_by_layer) { if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z < - this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - + this->get_layer(pair.first)->print_z - + this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() * target_flow_height_factor - EPSILON || intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()], layer_area_covered_by_candidates[pair.first]) @@ -1756,35 +1764,34 @@ void PrintObject::bridge_over_infill() } // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. - auto gather_areas_w_depth = - [](const PrintObject *po, int lidx, float target_flow_height) { - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons not_sparse_infill{}; - double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; - for (int i = int(lidx) - 1; i >= 0; --i) { - // Stop iterating if layer is lower than bottom_z. - const Layer *layer = po->get_layer(i); - if (layer->print_z < bottom_z) - break; + auto gather_areas_w_depth = [target_flow_height_factor](const PrintObject *po, int lidx, float target_flow_height) { + // Gather lower layers sparse infill areas, to depth defined by used bridge flow + Polygons lower_layers_sparse_infill{}; + Polygons not_sparse_infill{}; + double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; + for (int i = int(lidx) - 1; i >= 0; --i) { + // Stop iterating if layer is lower than bottom_z. + const Layer *layer = po->get_layer(i); + if (layer->print_z < bottom_z) + break; - for (const LayerRegion *region : layer->regions()) { - bool has_low_density = region->region().config().fill_density.value < 100; - for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density) { - Polygons p = to_polygons(surface.expolygon); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); - } else { - Polygons p = to_polygons(surface.expolygon); - not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); - } + for (const LayerRegion *region : layer->regions()) { + bool has_low_density = region->region().config().fill_density.value < 100; + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type == stInternal && has_low_density) { + Polygons p = to_polygons(surface.expolygon); + lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); + } else { + Polygons p = to_polygons(surface.expolygon); + not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); } } - lower_layers_sparse_infill = union_(lower_layers_sparse_infill); } + lower_layers_sparse_infill = union_(lower_layers_sparse_infill); + } - return diff(lower_layers_sparse_infill, not_sparse_infill); - }; + return diff(lower_layers_sparse_infill, not_sparse_infill); + }; // LAMBDA do determine optimal bridging angle auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern) { @@ -2052,7 +2059,7 @@ void PrintObject::bridge_over_infill() // Gather deep infill areas, where thick bridges fit coordf_t thick_bridges_depth = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height(); - Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth); + Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth * 0.5); // Now also remove area that has been already filled on lower layers by bridging expansion - For this // reason we did the clustering of layers per thread. @@ -2090,26 +2097,18 @@ void PrintObject::bridge_over_infill() if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, area_to_be_bridge); - boundary_area = closing(boundary_area, 0.3 * flow.scaled_spacing()); - Polylines boundary_plines = to_polylines(boundary_area); - double bridging_angle = 0; - Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); - for (const CandidateSurface &s : expanded_surfaces) { - if (!intersection(s.new_polys, tmp_expanded_area).empty()) { - bridging_angle = s.bridge_angle; - break; - } - } - if (bridging_angle == 0) { - if (!anchors.empty()) { - bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), - candidate.region->region().config().fill_pattern.value); - } else { - // use expansion boundaries as anchors. - // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. - bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine); - } + area_to_be_bridge = expand(area_to_be_bridge, flow.scaled_spacing()); + Polygons boundary_area = union_(expansion_area, area_to_be_bridge); + Polylines boundary_plines = to_polylines(boundary_area); + + double bridging_angle = 0; + if (!anchors.empty()) { + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), + candidate.region->region().config().fill_pattern.value); + } else { + // use expansion boundaries as anchors. + // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine); } boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end()); @@ -2117,6 +2116,23 @@ void PrintObject::bridge_over_infill() boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10))); } Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + + // Check collision with other expanded surfaces + { + bool reconstruct = false; + Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing()); + for (const CandidateSurface &s : expanded_surfaces) { + if (!intersection(s.new_polys, tmp_expanded_area).empty()) { + bridging_angle = s.bridge_angle; + reconstruct = true; + break; + } + } + if (reconstruct) { + bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + } + } + bridging_area = intersection(bridging_area, boundary_area); bridging_area = opening(bridging_area, flow.scaled_spacing()); expansion_area = diff(expansion_area, bridging_area); @@ -2124,7 +2140,7 @@ void PrintObject::bridge_over_infill() #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_expanded_bridging", - to_lines(layer->lslices), to_lines(candidate.original_surface->expolygon), to_lines(candidate.new_polys), + to_lines(layer->lslices), to_lines(boundary_plines), to_lines(candidate.new_polys), to_lines(bridging_area)); #endif From f2f9b890961c6f8b0b3b78e22694ceebd962491e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 14 Mar 2023 19:32:27 +0100 Subject: [PATCH 078/201] Fix of #9963 Bridge angle not accept degree but rad Fixes SPE-1583 --- src/libslic3r/LayerRegion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6017f393e..073907c8c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -378,7 +378,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly const double custom_angle = this->region().config().bridge_angle.value; const auto params = Algorithm::RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps); bridges.surfaces = custom_angle > 0 ? - expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, custom_angle) : + expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, Geometry::deg2rad(custom_angle)) : expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params); BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; #if 0 From c585aaa1cbd75c4cd05ff453e4eba6b4eb4b6ec7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 14 Mar 2023 19:33:24 +0100 Subject: [PATCH 079/201] TriangleMeshSlicer.cpp: Fixed compilation of debug output --- src/libslic3r/TriangleMeshSlicer.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index b91d1559f..7faa79435 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -34,6 +34,10 @@ // #define SLIC3R_DEBUG_SLICE_PROCESSING +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + #define DEBUG_INTERSECTIONLINE +#endif + #if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING) #include "SVG.hpp" #endif @@ -125,7 +129,7 @@ public: }; uint32_t flags { 0 }; -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE enum class Source { BottomPlane, TopPlane, @@ -1446,19 +1450,19 @@ static std::vector make_slab_loops( for (const IntersectionLine &l : lines.at_slice[slice_below]) if (l.edge_type != IntersectionLine::FacetEdgeType::Top) { in.emplace_back(l); -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE in.back().source = IntersectionLine::Source::BottomPlane; #endif // DEBUG_INTERSECTIONLINE } } { // Edges in between slice_below and slice_above. -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE size_t old_size = in.size(); #endif // DEBUG_INTERSECTIONLINE // Edge IDs of end points on in-between lines that touch the layer above are already increased with num_edges. append(in, lines.between_slices[line_idx]); -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE for (auto it = in.begin() + old_size; it != in.end(); ++ it) { assert(it->edge_type == IntersectionLine::FacetEdgeType::Slab); it->source = IntersectionLine::Source::Slab; @@ -1476,7 +1480,7 @@ static std::vector make_slab_loops( l.edge_a_id += num_edges; if (l.edge_b_id >= 0) l.edge_b_id += num_edges; -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE l.source = IntersectionLine::Source::TopPlane; #endif // DEBUG_INTERSECTIONLINE } From ca5b310e433013204878f6f78a5d56fd893ebebc Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 15 Mar 2023 11:25:17 +0100 Subject: [PATCH 080/201] Fix: moving with text object(not volume) over build plate by canvas dragging --- src/libslic3r/Model.cpp | 19 +++++++++++++++++++ src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 18 ++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 9 --------- src/slic3r/GUI/SurfaceDrag.cpp | 4 ++++ 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e1574e60..6fdec8b14 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1397,6 +1397,25 @@ void ModelObject::clone_for_cut(ModelObject** obj) (*obj)->input_file.clear(); } +bool ModelVolume::is_the_only_one_part() const +{ + if (m_type != ModelVolumeType::MODEL_PART) + return false; + if (object == nullptr) + return false; + for (const ModelVolume *v : object->volumes) { + if (v == nullptr) + continue; + // is this volume? + if (v->id() == this->id()) + continue; + // exist another model part in object? + if (v->type() == ModelVolumeType::MODEL_PART) + return false; + } + return true; +} + void ModelVolume::reset_extra_facets() { this->supported_facets.reset(); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 08fa79481..3fd9f21bc 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -835,6 +835,7 @@ public: bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } bool is_text() const { return text_configuration.has_value(); } + bool is_the_only_one_part() const; // behave like an object t_model_material_id material_id() const { return m_material_id; } void reset_extra_facets(); void apply_tolerance(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 88ccde4bb..bc0b5e811 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1169,7 +1169,8 @@ void GLGizmoEmboss::close() m_volume->text_configuration.has_value() && priv::is_text_empty(m_text)) { Plater &p = *wxGetApp().plater(); - if (is_text_object(m_volume)) { + // is the text object? + if (m_volume->is_the_only_one_part()) { // delete whole object p.remove(m_parent.get_selection().get_object_idx()); } else { @@ -1912,7 +1913,7 @@ void GLGizmoEmboss::draw_font_list() void GLGizmoEmboss::draw_model_type() { - bool is_last_solid_part = is_text_object(m_volume); + bool is_last_solid_part = m_volume->is_the_only_one_part(); std::string title = _u8L("Text is to object"); if (is_last_solid_part) { ImVec4 color{.5f, .5f, .5f, 1.f}; @@ -2945,7 +2946,7 @@ void GLGizmoEmboss::draw_advanced() // input surface distance bool allowe_surface_distance = !m_volume->text_configuration->style.prop.use_surface && - !is_text_object(m_volume); + !m_volume->is_the_only_one_part(); std::optional &distance = font_prop.distance; float prev_distance = distance.has_value() ? *distance : .0f, min_distance = -2 * font_prop.emboss, @@ -3336,17 +3337,6 @@ bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool dis ); } -bool GLGizmoEmboss::is_text_object(const ModelVolume *text) { - if (text == nullptr) return false; - if (!text->text_configuration.has_value()) return false; - if (text->type() != ModelVolumeType::MODEL_PART) return false; - for (const ModelVolume *v : text->get_object()->volumes) { - if (v == text) continue; - if (v->type() == ModelVolumeType::MODEL_PART) return false; - } - return true; -} - ///////////// // priv namespace implementation /////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 7e7c2aa16..730b7354c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -328,15 +328,6 @@ private: // only temporary solution static const std::string M_ICON_FILENAME; - -public: - ///

- /// Check if text is last solid part of object - /// TODO: move to emboss gui utils - /// - /// Model volume of Text - /// True when object otherwise False - static bool is_text_object(const ModelVolume *text); }; } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 0f2f1706a..f32113698 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -116,6 +116,10 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, if (object == nullptr || instance == nullptr || volume == nullptr) return false; + // allowed drag&drop by canvas for object + if (volume->is_the_only_one_part()) + return false; + const ModelVolumePtrs &volumes = object->volumes; std::vector allowed_volumes_id; if (volumes.size() > 1) { From f65c0b260099dab31f3d96e0131ae7c53997d24e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 15 Mar 2023 13:16:42 +0100 Subject: [PATCH 081/201] Rewrite missing supports alert message, now it should be translation-friendly --- src/libslic3r/Print.cpp | 88 +++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 370fa3ba2..a51091be1 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -14,11 +14,13 @@ #include "GCode/WipeTower.hpp" #include "Utils.hpp" #include "BuildVolume.hpp" +#include "format.hpp" #include #include #include +#include #include #include #include @@ -1182,12 +1184,49 @@ void Print::alert_when_supports_needed() break; case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: message = L("low bed adhesion"); break; case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: message = L("floating object part"); break; - case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("thin fragile section"); break; + case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("thin fragile part"); break; } return message; }; + // TRN this translation rule is used to translate lists of uknown size on single line. The first argument is element of the list, + // the second argument may be element or rest of the list. + auto single_line_list_rule = L("%1%, %2%"); + auto multiline_list_rule = "%1%\n%2%"; + + auto elements_to_translated_list = [](const std::vector &translated_elements, std::string expansion_rule) { + if (expansion_rule.find("%1%") == expansion_rule.npos || expansion_rule.find("%2%") == expansion_rule.npos) { + BOOST_LOG_TRIVIAL(error) << "INCORRECT EXPANSION RULE FOR LIST TRANSLATION: " << expansion_rule + << " - IT SHOULD CONTAIN %1% and %2%!"; + expansion_rule = "%1% %2%"; + } + if (translated_elements.size() == 0) { + return std::string{}; + } + if (translated_elements.size() == 1) { + return translated_elements.front(); + } + + std::string translated_list = expansion_rule; + for (int i = 0; i < translated_elements.size() - 1; i++) { + auto first_elem = translated_list.find("%1%"); + assert(first_elem != translated_list.npos); + translated_list.replace(first_elem, 3, translated_elements[i]); + + // expand the translated list by another application of the same rule + auto second_elem = translated_list.find("%2%"); + assert(second_elem != translated_list.npos); + if (i < translated_elements.size() - 2) { + translated_list.replace(second_elem, 3, expansion_rule); + } else { + translated_list.replace(second_elem, 3, translated_elements[i + 1]); + } + } + + return translated_list; + }; + // vector of pairs of object and its issues, where each issue is a pair of type and critical flag std::vector>>> objects_isssues; @@ -1207,45 +1246,60 @@ void Print::alert_when_supports_needed() } } - bool recommend_brim = false; + bool recommend_brim = false; std::map, std::vector> po_by_support_issues; for (const auto &obj : objects_isssues) { for (const auto &issue : obj.second) { po_by_support_issues[issue].push_back(obj.first); - if (issue.first == SupportSpotsGenerator::SupportPointCause::SeparationFromBed && !obj.first->has_brim()){ + if (issue.first == SupportSpotsGenerator::SupportPointCause::SeparationFromBed && !obj.first->has_brim()) { recommend_brim = true; } } } - auto message = L("Detected print stability issues") + ": \n"; + // TRN first argument is a list of detected issues + auto top_level_message = L("Detected print stability issues:\n%1%"); + + std::vector>> message_elements; if (objects_isssues.size() > po_by_support_issues.size()) { // there are more objects than causes, group by issues for (const auto &issue : po_by_support_issues) { - message += "\n" + issue_to_alert_message(issue.first.first, issue.first.second) + " >> "; + auto &pair = message_elements.emplace_back(issue_to_alert_message(issue.first.first, issue.first.second), + std::vector{}); for (const auto &obj : issue.second) { - message += obj->m_model_object->name + ", "; + pair.second.push_back(obj->m_model_object->name); } - message.pop_back(); - message.pop_back(); // remove , - message += ".\n"; } } else { // more causes than objects, group by objects for (const auto &obj : objects_isssues) { - message += "\n" + L("Object") + " " + obj.first->model_object()->name + " << "; + auto &pair = message_elements.emplace_back(obj.first->model_object()->name, std::vector{}); for (const auto &issue : obj.second) { - message += issue_to_alert_message(issue.first, issue.second) + ", "; + pair.second.push_back(issue_to_alert_message(issue.first, issue.second)); } - message.pop_back(); - message.pop_back(); // remove , - message += ".\n"; } } - bool brim_or_supp = recommend_brim && po_by_support_issues.size() < 2; - auto brim_part = " " + (brim_or_supp ? L("or") : L("and")) + " " + L("brim"); - message += "\n" + L("Consider enabling supports") + (recommend_brim ? brim_part : "") + "."; + // first, gather sublements into single line list, store in first subelement + for (auto &pair : message_elements) { + pair.second.front() = elements_to_translated_list(pair.second, single_line_list_rule); + } + + // then gather elements to create multiline list + std::vector lines = {}; + for (auto &pair : message_elements) { + lines.push_back(""); // empty line for readability + lines.push_back(pair.first); + lines.push_back(pair.second.front()); + } + + lines.push_back(""); + lines.push_back(L("Consider enabling supports.")); + if (recommend_brim) { + lines.push_back(L("Also consider enabling brim.")); + } + + auto message = Slic3r::format(top_level_message, elements_to_translated_list(lines, multiline_list_rule)); if (objects_isssues.size() > 0) { this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, message); From 46a558129d3fd213995016e08676ae63011649cb Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 15 Mar 2023 14:12:54 +0100 Subject: [PATCH 082/201] improve translation descriptions in support alerts --- src/libslic3r/Print.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a51091be1..4030e381a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1173,25 +1173,33 @@ void Print::alert_when_supports_needed() auto issue_to_alert_message = [](SupportSpotsGenerator::SupportPointCause cause, bool critical) { std::string message; switch (cause) { - case SupportSpotsGenerator::SupportPointCause::LongBridge: message = L("long bridging extrusions"); break; - case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: message = L("floating bridge anchors"); break; + //TRN Alert when support is needed. Describes that the model has long bridging extrusions which may print badly + case SupportSpotsGenerator::SupportPointCause::LongBridge: message = L("Long bridging extrusions"); break; + //TRN Alert when support is needed. Describes bridge anchors/turns in the air, which will definitely print badly + case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: message = L("Floating bridge anchors"); break; case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion: if (critical) { - message = L("collapsing overhang"); + //TRN Alert when support is needed. Describes that the print has large overhang area which will print badly or not print at all. + message = L("Collapsing overhang"); } else { - message = L("loose extrusions"); + //TRN Alert when support is needed. Describes extrusions that are not supported enough and come out curled or loose. + message = L("Loose extrusions"); } break; - case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: message = L("low bed adhesion"); break; - case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: message = L("floating object part"); break; - case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("thin fragile part"); break; + //TRN Alert when support is needed. Describes that the print has low bed adhesion and may became loose. + case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: message = L("Low bed adhesion"); break; + //TRN Alert when support is needed. Describes that the object has part that is not connected to the bed and will not print at all without supports. + case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: message = L("Floating object part"); break; + //TRN Alert when support is needed. Describes that the object has thin part that may brake during printing + case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("Thin fragile part"); break; } return message; }; // TRN this translation rule is used to translate lists of uknown size on single line. The first argument is element of the list, - // the second argument may be element or rest of the list. + // the second argument may be element or rest of the list. For most languages, this does not need translation, but some use different + // separator than comma and some use blank space in front of the separator. auto single_line_list_rule = L("%1%, %2%"); auto multiline_list_rule = "%1%\n%2%"; @@ -1257,9 +1265,6 @@ void Print::alert_when_supports_needed() } } - // TRN first argument is a list of detected issues - auto top_level_message = L("Detected print stability issues:\n%1%"); - std::vector>> message_elements; if (objects_isssues.size() > po_by_support_issues.size()) { // there are more objects than causes, group by issues @@ -1299,7 +1304,8 @@ void Print::alert_when_supports_needed() lines.push_back(L("Also consider enabling brim.")); } - auto message = Slic3r::format(top_level_message, elements_to_translated_list(lines, multiline_list_rule)); + // TRN Alert message for detected print issues. first argument is a list of detected issues. + auto message = Slic3r::format(L("Detected print stability issues:\n%1%"), elements_to_translated_list(lines, multiline_list_rule)); if (objects_isssues.size() > 0) { this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, message); From 5277ed502d45d22d75c634c3d3bd209f20d57215 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 15 Mar 2023 16:13:06 +0100 Subject: [PATCH 083/201] improve anchoring on lower layer solids, cut the expanded bridge strictly, which hopefully fixes tests --- src/libslic3r/PrintObject.cpp | 37 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ccdc7a914..5e07ebc69 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1711,7 +1711,7 @@ void PrintObject::bridge_over_infill() // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; - float target_flow_height_factor = 0.5; + float target_flow_height_factor = 0.75; { std::vector layers_with_candidates; std::map layer_area_covered_by_candidates; @@ -1789,7 +1789,7 @@ void PrintObject::bridge_over_infill() } lower_layers_sparse_infill = union_(lower_layers_sparse_infill); } - + lower_layers_sparse_infill = closing(lower_layers_sparse_infill, SCALED_EPSILON); return diff(lower_layers_sparse_infill, not_sparse_infill); }; @@ -2032,7 +2032,8 @@ void PrintObject::bridge_over_infill() }; tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = static_cast(this), - &surfaces_by_layer, &clustered_layers_for_threads, + target_flow_height_factor, &surfaces_by_layer, + &clustered_layers_for_threads, gather_areas_w_depth, &infill_lines, determine_bridging_angle, construct_anchored_polygon]( @@ -2058,12 +2059,12 @@ void PrintObject::bridge_over_infill() }); // Gather deep infill areas, where thick bridges fit - coordf_t thick_bridges_depth = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height(); - Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth * 0.5); + coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor; + Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height); // Now also remove area that has been already filled on lower layers by bridging expansion - For this // reason we did the clustering of layers per thread. - double bottom_z = layer->print_z - thick_bridges_depth - EPSILON; + double bottom_z = layer->print_z - target_flow_height - EPSILON; if (job_idx > 0) { for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; @@ -2080,10 +2081,14 @@ void PrintObject::bridge_over_infill() // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors Polygons expansion_area; + Polygons total_fill_area; for (const LayerRegion *region : layer->regions()) { - auto polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); - expansion_area.insert(expansion_area.end(), polys.begin(), polys.end()); + Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); + expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end()); + Polygons fill_polys = to_polygons(region->fill_expolygons()); + total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end()); } + total_fill_area = closing(total_fill_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); @@ -2098,10 +2103,16 @@ void PrintObject::bridge_over_infill() continue; area_to_be_bridge = expand(area_to_be_bridge, flow.scaled_spacing()); - Polygons boundary_area = union_(expansion_area, area_to_be_bridge); - Polylines boundary_plines = to_polylines(boundary_area); - - double bridging_angle = 0; + + Polylines boundary_plines = to_polylines(expand(total_fill_area, 1.3 * flow.scaled_spacing())); + { + Polylines expansion_plines = to_polylines(expansion_area); + boundary_plines.insert(boundary_plines.end(), expansion_plines.begin(), expansion_plines.end()); + Polylines expanded_expansion_plines = to_polylines(expand(expansion_area, 1.3 * flow.scaled_spacing())); + boundary_plines.insert(boundary_plines.end(), expanded_expansion_plines.begin(), expanded_expansion_plines.end()); + } + + double bridging_angle = 0; if (!anchors.empty()) { bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), candidate.region->region().config().fill_pattern.value); @@ -2133,8 +2144,8 @@ void PrintObject::bridge_over_infill() } } - bridging_area = intersection(bridging_area, boundary_area); bridging_area = opening(bridging_area, flow.scaled_spacing()); + bridging_area = intersection(bridging_area, total_fill_area); expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL From 66abc34e8869e0a1535a06e23d12a67de3f0e767 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 15 Mar 2023 18:37:44 +0100 Subject: [PATCH 084/201] Fixed wrong direction of bridges after "ensure vertical wall thickness" rework. Fixes SPE-1598 --- src/libslic3r/LayerRegion.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 073907c8c..6374766cd 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -291,22 +291,24 @@ Surfaces expand_bridges_detect_orientations( uint32_t src_id = it->src_id; for (++ it; it != bridge_expansions.end() && it->src_id == src_id; ++ it) ; } - for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) { - acc.clear(); - for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2) - if (group_id(bridge_id) == bridge_id) { - append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon))); - auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin; - assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2); - for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion) - append(acc, to_polygons(std::move(it_bridge_expansion->expolygon))); - } - //FIXME try to be smart and pick the best bridging angle for all? - templ.bridge_angle = bridges[bridge_id].angle; - // without safety offset, artifacts are generated (GH #2494) - for (ExPolygon &ex : union_safety_offset_ex(acc)) - out.emplace_back(templ, std::move(ex)); - } + for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) + if (group_id(bridge_id) == bridge_id) { + // Head of the group. + acc.clear(); + for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2) + if (group_id(bridge_id2) == bridge_id) { + append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon))); + auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin; + assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2); + for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion) + append(acc, to_polygons(std::move(it_bridge_expansion->expolygon))); + } + //FIXME try to be smart and pick the best bridging angle for all? + templ.bridge_angle = bridges[bridge_id].angle; + // without safety offset, artifacts are generated (GH #2494) + for (ExPolygon &ex : union_safety_offset_ex(acc)) + out.emplace_back(templ, std::move(ex)); + } } // Clip the shells by the expanded bridges. From 379c6366de803b62cc50cd57c648432a2ce55611 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 15 Mar 2023 20:51:09 +0100 Subject: [PATCH 085/201] XL resources --- .../profiles/PrusaResearch/XL_thumbnail.png | Bin 0 -> 64253 bytes resources/profiles/PrusaResearch/xl.svg | 123 ++++++++++++++++++ resources/profiles/PrusaResearch/xl_bed.stl | Bin 0 -> 25884 bytes 3 files changed, 123 insertions(+) create mode 100644 resources/profiles/PrusaResearch/XL_thumbnail.png create mode 100644 resources/profiles/PrusaResearch/xl.svg create mode 100644 resources/profiles/PrusaResearch/xl_bed.stl diff --git a/resources/profiles/PrusaResearch/XL_thumbnail.png b/resources/profiles/PrusaResearch/XL_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1d0403c6c6191bab56e8bf262af0e571f9b0d3 GIT binary patch literal 64253 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 z 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 x9^jIr!A}w&(OkH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/profiles/PrusaResearch/xl_bed.stl b/resources/profiles/PrusaResearch/xl_bed.stl new file mode 100644 index 0000000000000000000000000000000000000000..2ddf41831757d36e32bfb6f1ef08f27600caa457 GIT binary patch literal 25884 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 Date: Wed, 15 Mar 2023 20:51:59 +0100 Subject: [PATCH 086/201] Sync with PrusaSlicer-settings. Added XL. --- resources/profiles/PrusaResearch.idx | 471 ++-- resources/profiles/PrusaResearch.ini | 3607 ++++++++++++++++++++++++-- 2 files changed, 3675 insertions(+), 403 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index ac12bef70..97d18c1be 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,231 +1,240 @@ -min_slic3r_version = 2.6.0-alpha1 -1.7.0-alpha0 Added profiles for Print With Smile filaments. -1.6.0-alpha2 Added profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. Updated acceleration settings for Prusa MINI. -1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. -1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. -min_slic3r_version = 2.5.0-alpha0 -1.5.7 Added filament profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. -1.5.6 Updated FW version notification (MK2.5/MK3 family). Added filament profile for Kimya PEBA-S. -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. -1.5.1 Renamed filament type "NYLON" to "PA". Updated Adura X profile. Updated PETG fan settings for Prusa MINI (removed fan ramp up). -1.5.0 Updated arachne parameters. Added profiles for Jessie filaments. -1.5.0-alpha1 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. -1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format. -min_slic3r_version = 2.4.0-rc -1.4.9 Updated FW version notification. -1.4.8 Added filament and SLA material profiles. Updated settings. -1.4.7 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. -1.4.6 Added SLA materials. Updated filament profiles. -1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code. -1.4.4 Added multiple Fiberlogy filament profiles. Updated Extrudr filament profiles. -1.4.3 Added new filament profiles and SLA materials. -1.4.2 Added SLA material profiles. -1.4.1 Updated firmware version. -1.4.0 Updated for the PrusaSlicer 2.4.0-rc release. Updated SLA material colors. -min_slic3r_version = 2.4.0-beta2 -1.4.0-beta3 Added material profiles for Prusament Resins. -1.4.0-beta2 Added SLA material colors. Updated BASF filament profiles. -min_slic3r_version = 2.4.0-beta0 -1.4.0-beta1 Updated pad wall slope angle for SLA printers. Updated Filatech Filacarbon profile for Prusa MINI. -1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S. -min_slic3r_version = 2.4.0-alpha0 -1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default. -1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles. -1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height). -1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S). -1.4.0-alpha4 Decreased Area Fill (SL1S). -1.4.0-alpha3 Updated SL1S tilt times. -1.4.0-alpha2 Updated Prusa MINI machine limits. -1.4.0-alpha1 Added new SL1S resin profiles. -1.4.0-alpha0 Bumped up config version. -1.3.0-alpha2 Added SL1S SPEED profiles. -1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values. -1.3.0-alpha0 Disabled thick bridges, updated support settings. -min_slic3r_version = 2.3.2-alpha0 -1.3.8 Updated FW version notification. -1.3.7 Updated firmware version. -1.3.6 Updated firmware version. -1.3.5 Added material profiles for Prusament Resins. -1.3.4 Added material profiles for new Prusament Resins. Added profiles for multiple BASF filaments. -1.3.3 Added multiple profiles for Filatech filaments. Added material profiles for SL1S SPEED. Updated SLA print settings. -1.3.2 Added material profiles for Prusament Resin. -1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S). -1.3.0 Added SL1S SPEED profiles. -min_slic3r_version = 2.3.0-rc1 -1.2.13 Updated FW version notification. -1.2.12 Updated firmware version. -1.2.11 Updated firmware version. -1.2.10 Added multiple profiles for Filatech filaments. Updated SLA print settings (pad wall slope angle). -1.2.9 Added material profiles for Prusament Resin. -1.2.8 Added multiple add:north and Extrudr filament profiles. -1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI. -1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber". -1.2.5 Updated firmware version. Added filament profiles. Various improvements. -1.2.4 Updated cost/density values in filament settings. Various changes in print settings. -1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles. -1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. -1.2.1 Updated FW version for MK2.5 family printers. -1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version. -min_slic3r_version = 2.3.0-beta2 -1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values. -min_slic3r_version = 2.3.0-beta0 -1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. -min_slic3r_version = 2.3.0-alpha4 -1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. -1.2.0-alpha0 Added filament spool weights -min_slic3r_version = 2.2.0-alpha3 -1.1.17 Updated FW version notification. -1.1.16 Updated firmware version. -1.1.15 Updated firmware version. -1.1.14 Updated firmware version. -1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. -1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. -1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. -1.1.10 Updated firmware version. -1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials. -1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles. -1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles. -1.1.6 Updated firmware version for MK2.5/S and MK3/S. -1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend -1.1.4 Added Prusament PC Blend filament profile. -1.1.3 Added SLA material and filament profile -1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET. -1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS. -1.1.1-beta Updated for PrusaSlicer 2.2.0-beta -1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc -1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. -# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, -# so they will see the print bed. -max_slic3r_version = 2.2.0-alpha2 -min_slic3r_version = 2.2.0-alpha0 -1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. -1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 -min_slic3r_version = 2.1.1-beta0 -1.0.13 Updated FW version notification. -1.0.12 Updated firmware version. -1.0.11 Updated firmware version. -1.0.10 Updated firmware version for MK2.5/S and MK3/S. -1.0.9 Updated firmware version for MK2.5/S and MK3/S. -1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog. -1.0.7 Updated layer height limits for MINI -1.0.6 Added Prusa MINI profiles -min_slic3r_version = 2.1.0-alpha0 -1.0.5 Added SLA materials -1.0.4 Updated firmware version and 0.25mm nozzle profiles -1.0.3 Added filament profiles -1.0.2 Added SLA materials -1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. -1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. -1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. -1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times -1.0.0-alpha2 Printer model and nozzle diameter check -1.0.0-alpha1 Added Prusament ASA profile -1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX -min_slic3r_version = 1.42.0-alpha6 -0.8.11 Updated firmware version. -0.8.10 Updated firmware version. -0.8.9 Updated firmware version for MK2.5/S and MK3/S. -0.8.8 Updated firmware version for MK2.5/S and MK3/S. -0.8.7 Updated firmware version -0.8.6 Updated firmware version for MK2.5/S and MK3/S -0.8.5 Updated SL1 printer and material settings -0.8.4 Added Prusament ASA profile -0.8.3 FW version and SL1 materials update -0.8.2 FFF and SL1 settings update -0.8.1 Output settings and SLA materials update -0.8.0 Updated for the PrusaSlicer 2.0.0 final release -0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S -0.8.0-rc1 Updated SLA profiles -0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release -0.8.0-beta4 Updated SLA profiles -0.8.0-beta3 Updated SLA profiles -0.8.0-beta2 Updated SLA profiles -0.8.0-beta1 Updated SLA profiles -0.8.0-beta Updated SLA profiles -0.8.0-alpha9 Updated SLA and FFF profiles -0.8.0-alpha8 Updated SLA profiles -0.8.0-alpha7 Updated SLA profiles -0.8.0-alpha6 Updated SLA profiles -min_slic3r_version = 1.42.0-alpha -0.8.0-alpha Updated SLA profiles -0.4.0-alpha4 Updated SLA profiles -0.4.0-alpha3 Update of SLA profiles -0.4.0-alpha2 First SLA profiles -min_slic3r_version = 1.41.3-alpha -0.4.12 Updated firmware version for MK2.5/S and MK3/S. -0.4.11 Updated firmware version for MK2.5/S and MK3/S. -0.4.10 Updated firmware version -0.4.9 Updated firmware version for MK2.5/S and MK3/S -0.4.8 MK2.5/3/S FW update -0.4.7 MK2/S/MMU FW update -0.4.6 Updated firmware versions for MK2.5/S and MK3/S -0.4.5 Enabled remaining time support for MK2/S/MMU1 -0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.1 New MK2.5S and MK3S FW versions -0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -min_slic3r_version = 1.41.1 -0.3.11 Updated firmware version for MK2.5/S and MK3/S. -0.3.10 Updated firmware version -0.3.9 Updated firmware version for MK2.5/S and MK3/S -0.3.8 MK2.5/3/S FW update -0.3.7 MK2/S/MMU FW update -0.3.6 Updated firmware versions for MK2.5 and MK3 -0.3.5 New MK2.5 and MK3 FW versions -0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.3.3 Prusament PETG released -0.3.2 New MK2.5 and MK3 FW versions -0.3.1 New MK2.5 and MK3 FW versions -0.3.0 New MK2.5 and MK3 FW version -min_slic3r_version = 1.41.0-alpha -0.2.9 New MK2.5 and MK3 FW versions -0.2.8 New MK2.5 and MK3 FW version -min_slic3r_version = 1.41.1 -0.2.7 New MK2.5 and MK3 FW version -0.2.6 Added MMU2 MK2.5 settings -min_slic3r_version = 1.41.0-alpha -0.2.5 Prusament is out - added prusament settings -0.2.4 Added soluble support profiles for MMU2 -0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit -0.2.2 Edited MMU2 Single mode purge line -0.2.1 Added PET and BVOH settings for MMU2 -0.2.0-beta5 Fixed MMU1 ramming parameters -0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower -0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 -0.2.0-beta2 Edited first layer speed and wipe tower position -0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles -0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. -0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references -0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version -0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 -0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. -0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost -0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material -0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 -0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters -min_slic3r_version = 1.40.0 -0.1.18 Updated firmware version -0.1.17 Updated firmware version for MK2.5/S and MK3/S -0.1.16 MK2.5/3/S FW update -0.1.15 MK2/S/MMU FW update -0.1.14 Updated firmware versions for MK2.5 and MK3 -0.1.13 New MK2.5 and MK3 FW versions -0.1.12 New MK2.5 and MK3 FW versions -0.1.11 fw version changed to 3.3.1 -0.1.10 MK3 jerk and acceleration update -0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles -0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes -0.1.7 Fixed errors in 0.25mm and 0.6mm profiles -0.1.6 Split the MK2.5 profile from the MK2S -min_slic3r_version = 1.40.0-beta -0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles -0.1.4 edited fw version, added z-raise after print -min_slic3r_version = 1.40.0-alpha -0.1.3 Fixed an incorrect position of the max_print_height parameter -0.1.2 Wipe tower changes -0.1.1 Minor print speed adjustments -0.1.0 Initial +min_slic3r_version = 2.6.0-alpha5 +1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. +min_slic3r_version = 2.6.0-alpha1 +1.7.0-alpha0 Added profiles for Print With Smile filaments. +1.6.0-alpha2 Added profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. Updated acceleration settings for Prusa MINI. +1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. +1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. +min_slic3r_version = 2.5.1-rc0 +1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles. +1.6.0 Added Original Prusa XL profiles. Updated acceleration settings for Prusa MINI. Updated infill/perimeter overlap values. +min_slic3r_version = 2.5.0-alpha0 +1.5.8 Added filament profile for Prusament PETG Tungsten 75%. Updated FW version notification. +1.5.7 Added filament profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. +1.5.6 Updated FW version notification (MK2.5/MK3 family). Added filament profile for Kimya PEBA-S. +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. +1.5.1 Renamed filament type "NYLON" to "PA". Updated Adura X profile. Updated PETG fan settings for Prusa MINI (removed fan ramp up). +1.5.0 Updated arachne parameters. Added profiles for Jessie filaments. +1.5.0-alpha1 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. +1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format. +min_slic3r_version = 2.4.0-rc +1.4.10 Updated FW version notification. +1.4.9 Updated FW version notification. +1.4.8 Added filament and SLA material profiles. Updated settings. +1.4.7 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. +1.4.6 Added SLA materials. Updated filament profiles. +1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code. +1.4.4 Added multiple Fiberlogy filament profiles. Updated Extrudr filament profiles. +1.4.3 Added new filament profiles and SLA materials. +1.4.2 Added SLA material profiles. +1.4.1 Updated firmware version. +1.4.0 Updated for the PrusaSlicer 2.4.0-rc release. Updated SLA material colors. +min_slic3r_version = 2.4.0-beta2 +1.4.0-beta3 Added material profiles for Prusament Resins. +1.4.0-beta2 Added SLA material colors. Updated BASF filament profiles. +min_slic3r_version = 2.4.0-beta0 +1.4.0-beta1 Updated pad wall slope angle for SLA printers. Updated Filatech Filacarbon profile for Prusa MINI. +1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S. +min_slic3r_version = 2.4.0-alpha0 +1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default. +1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles. +1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height). +1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S). +1.4.0-alpha4 Decreased Area Fill (SL1S). +1.4.0-alpha3 Updated SL1S tilt times. +1.4.0-alpha2 Updated Prusa MINI machine limits. +1.4.0-alpha1 Added new SL1S resin profiles. +1.4.0-alpha0 Bumped up config version. +1.3.0-alpha2 Added SL1S SPEED profiles. +1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values. +1.3.0-alpha0 Disabled thick bridges, updated support settings. +min_slic3r_version = 2.3.2-alpha0 +1.3.9 Updated FW version notification. +1.3.8 Updated FW version notification. +1.3.7 Updated firmware version. +1.3.6 Updated firmware version. +1.3.5 Added material profiles for Prusament Resins. +1.3.4 Added material profiles for new Prusament Resins. Added profiles for multiple BASF filaments. +1.3.3 Added multiple profiles for Filatech filaments. Added material profiles for SL1S SPEED. Updated SLA print settings. +1.3.2 Added material profiles for Prusament Resin. +1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S). +1.3.0 Added SL1S SPEED profiles. +min_slic3r_version = 2.3.0-rc1 +1.2.14 Updated FW version notification. +1.2.13 Updated FW version notification. +1.2.12 Updated firmware version. +1.2.11 Updated firmware version. +1.2.10 Added multiple profiles for Filatech filaments. Updated SLA print settings (pad wall slope angle). +1.2.9 Added material profiles for Prusament Resin. +1.2.8 Added multiple add:north and Extrudr filament profiles. +1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI. +1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber". +1.2.5 Updated firmware version. Added filament profiles. Various improvements. +1.2.4 Updated cost/density values in filament settings. Various changes in print settings. +1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles. +1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. +1.2.1 Updated FW version for MK2.5 family printers. +1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version. +min_slic3r_version = 2.3.0-beta2 +1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values. +min_slic3r_version = 2.3.0-beta0 +1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. +min_slic3r_version = 2.3.0-alpha4 +1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. +1.2.0-alpha0 Added filament spool weights +min_slic3r_version = 2.2.0-alpha3 +1.1.17 Updated FW version notification. +1.1.16 Updated firmware version. +1.1.15 Updated firmware version. +1.1.14 Updated firmware version. +1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. +1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. +1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. +1.1.10 Updated firmware version. +1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials. +1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles. +1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles. +1.1.6 Updated firmware version for MK2.5/S and MK3/S. +1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend +1.1.4 Added Prusament PC Blend filament profile. +1.1.3 Added SLA material and filament profile +1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET. +1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS. +1.1.1-beta Updated for PrusaSlicer 2.2.0-beta +1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc +1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. +# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, +# so they will see the print bed. +max_slic3r_version = 2.2.0-alpha2 +min_slic3r_version = 2.2.0-alpha0 +1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. +1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 +min_slic3r_version = 2.1.1-beta0 +1.0.13 Updated FW version notification. +1.0.12 Updated firmware version. +1.0.11 Updated firmware version. +1.0.10 Updated firmware version for MK2.5/S and MK3/S. +1.0.9 Updated firmware version for MK2.5/S and MK3/S. +1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog. +1.0.7 Updated layer height limits for MINI +1.0.6 Added Prusa MINI profiles +min_slic3r_version = 2.1.0-alpha0 +1.0.5 Added SLA materials +1.0.4 Updated firmware version and 0.25mm nozzle profiles +1.0.3 Added filament profiles +1.0.2 Added SLA materials +1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. +1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. +1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. +1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times +1.0.0-alpha2 Printer model and nozzle diameter check +1.0.0-alpha1 Added Prusament ASA profile +1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX +min_slic3r_version = 1.42.0-alpha6 +0.8.11 Updated firmware version. +0.8.10 Updated firmware version. +0.8.9 Updated firmware version for MK2.5/S and MK3/S. +0.8.8 Updated firmware version for MK2.5/S and MK3/S. +0.8.7 Updated firmware version +0.8.6 Updated firmware version for MK2.5/S and MK3/S +0.8.5 Updated SL1 printer and material settings +0.8.4 Added Prusament ASA profile +0.8.3 FW version and SL1 materials update +0.8.2 FFF and SL1 settings update +0.8.1 Output settings and SLA materials update +0.8.0 Updated for the PrusaSlicer 2.0.0 final release +0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S +0.8.0-rc1 Updated SLA profiles +0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release +0.8.0-beta4 Updated SLA profiles +0.8.0-beta3 Updated SLA profiles +0.8.0-beta2 Updated SLA profiles +0.8.0-beta1 Updated SLA profiles +0.8.0-beta Updated SLA profiles +0.8.0-alpha9 Updated SLA and FFF profiles +0.8.0-alpha8 Updated SLA profiles +0.8.0-alpha7 Updated SLA profiles +0.8.0-alpha6 Updated SLA profiles +min_slic3r_version = 1.42.0-alpha +0.8.0-alpha Updated SLA profiles +0.4.0-alpha4 Updated SLA profiles +0.4.0-alpha3 Update of SLA profiles +0.4.0-alpha2 First SLA profiles +min_slic3r_version = 1.41.3-alpha +0.4.12 Updated firmware version for MK2.5/S and MK3/S. +0.4.11 Updated firmware version for MK2.5/S and MK3/S. +0.4.10 Updated firmware version +0.4.9 Updated firmware version for MK2.5/S and MK3/S +0.4.8 MK2.5/3/S FW update +0.4.7 MK2/S/MMU FW update +0.4.6 Updated firmware versions for MK2.5/S and MK3/S +0.4.5 Enabled remaining time support for MK2/S/MMU1 +0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.1 New MK2.5S and MK3S FW versions +0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +min_slic3r_version = 1.41.1 +0.3.11 Updated firmware version for MK2.5/S and MK3/S. +0.3.10 Updated firmware version +0.3.9 Updated firmware version for MK2.5/S and MK3/S +0.3.8 MK2.5/3/S FW update +0.3.7 MK2/S/MMU FW update +0.3.6 Updated firmware versions for MK2.5 and MK3 +0.3.5 New MK2.5 and MK3 FW versions +0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.3.3 Prusament PETG released +0.3.2 New MK2.5 and MK3 FW versions +0.3.1 New MK2.5 and MK3 FW versions +0.3.0 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.0-alpha +0.2.9 New MK2.5 and MK3 FW versions +0.2.8 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.1 +0.2.7 New MK2.5 and MK3 FW version +0.2.6 Added MMU2 MK2.5 settings +min_slic3r_version = 1.41.0-alpha +0.2.5 Prusament is out - added prusament settings +0.2.4 Added soluble support profiles for MMU2 +0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit +0.2.2 Edited MMU2 Single mode purge line +0.2.1 Added PET and BVOH settings for MMU2 +0.2.0-beta5 Fixed MMU1 ramming parameters +0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower +0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 +0.2.0-beta2 Edited first layer speed and wipe tower position +0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles +0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. +0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references +0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version +0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 +0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. +0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost +0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material +0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 +0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters +min_slic3r_version = 1.40.0 +0.1.18 Updated firmware version +0.1.17 Updated firmware version for MK2.5/S and MK3/S +0.1.16 MK2.5/3/S FW update +0.1.15 MK2/S/MMU FW update +0.1.14 Updated firmware versions for MK2.5 and MK3 +0.1.13 New MK2.5 and MK3 FW versions +0.1.12 New MK2.5 and MK3 FW versions +0.1.11 fw version changed to 3.3.1 +0.1.10 MK3 jerk and acceleration update +0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles +0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes +0.1.7 Fixed errors in 0.25mm and 0.6mm profiles +0.1.6 Split the MK2.5 profile from the MK2S +min_slic3r_version = 1.40.0-beta +0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles +0.1.4 edited fw version, added z-raise after print +min_slic3r_version = 1.40.0-alpha +0.1.3 Fixed an incorrect position of the max_print_height parameter +0.1.2 Wipe tower changes +0.1.1 Minor print speed adjustments +0.1.0 Initial diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index aaa11d678..1776d5e95 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -1,11 +1,11 @@ -# Print profiles for the Prusa Research printers. +# Print profiles for Prusa Research printers. [vendor] # Vendor name will be shown by the Config Wizard. 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.7.0-alpha0 +config_version = 1.7.0-alpha1 # 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% @@ -14,6 +14,15 @@ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # 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:XL] +name = Original Prusa XL +variants = 0.6; 0.25; 0.3; 0.4; 0.5; 0.8 +technology = FFF +family = XL +bed_model = xl_bed.stl +bed_texture = xl.svg +default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6 + [printer_model:MINI] name = Original Prusa MINI && MINI+ variants = 0.4; 0.25; 0.6; 0.8 @@ -183,7 +192,7 @@ notes = overhangs = 1 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode perimeters = 2 perimeter_extruder = 1 perimeter_extrusion_width = 0.45 @@ -228,6 +237,7 @@ support_material_bottom_interface_layers = 0 thin_walls = 0 top_infill_extrusion_width = 0.45 top_solid_infill_speed = 40 +top_fill_pattern = monotoniclines travel_speed = 180 travel_speed_z = 12 wipe_tower = 1 @@ -242,13 +252,7 @@ bottom_solid_min_thickness = 0.5 gcode_label_objects = 1 infill_anchor = 2.5 infill_anchor_max = 12 -wall_transition_angle = 10 -wall_transition_filter_deviation = 25% -wall_transition_length = 0.4 -wall_distribution_count = 1 -min_bead_width = 85% enable_dynamic_overhang_speeds = 1 -top_fill_pattern = monotoniclines [print:*MK3*] fill_pattern = grid @@ -257,6 +261,29 @@ travel_speed = 180 wipe_tower_x = 170 wipe_tower_y = 125 +[print:*XL*] +inherits = *common* +single_extruder_multi_material_priming = 0 +travel_speed = 400 +travel_speed_z = 10 +fill_density = 15% +default_acceleration = 1250 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +infill_anchor = 2 +perimeter_acceleration = 1000 +fill_pattern = grid +skirts = 0 +extruder_clearance_height = 15 +extruder_clearance_radius = 60 +first_layer_speed = 25 +support_material_threshold = 45 +raft_first_layer_density = 80% +## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode + [print:*MK306*] inherits = *MK3* fill_pattern = gyroid @@ -297,13 +324,6 @@ thick_bridges = 0 bridge_flow_ratio = 1 bridge_speed = 20 wipe_tower_bridging = 6 -wall_transition_angle = 10 -wall_transition_filter_deviation = 25% -wall_transition_length = 0.25 -wall_distribution_count = 1 -min_bead_width = 85% -infill_overlap = 10% -dynamic_overhang_speeds[0] = 20,20,15,15 [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -333,6 +353,49 @@ solid_infill_speed = 40 infill_acceleration = 800 first_layer_acceleration = 500 +[print:*0.25nozzleXL*] +inherits = *0.25nozzleMK3* +infill_speed = 40 +solid_infill_speed = 40 +infill_acceleration = 800 +first_layer_acceleration = 500 +infill_anchor = 1 +perimeters = 3 +brim_separation = 0 + +[print:*0.3nozzle*] +external_perimeter_extrusion_width = 0.33 +extrusion_width = 0.33 +first_layer_extrusion_width = 0.4 +infill_extrusion_width = 0.33 +perimeter_extrusion_width = 0.33 +solid_infill_extrusion_width = 0.33 +top_infill_extrusion_width = 0.3 +support_material_extrusion_width = 0.3 +fill_density = 20% +perimeters = 3 +infill_anchor = 1.5 + +[print:*0.5nozzle*] +external_perimeter_extrusion_width = 0.55 +extrusion_width = 0.55 +first_layer_extrusion_width = 0.55 +infill_extrusion_width = 0.55 +perimeter_extrusion_width = 0.55 +solid_infill_extrusion_width = 0.55 +top_infill_extrusion_width = 0.5 +support_material_extrusion_width = 0.4 +support_material_contact_distance = 0.2 +support_material_xy_spacing = 80% +support_material_interface_spacing = 0.2 +support_material_spacing = 2.2 +raft_first_layer_expansion = 2 +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +infill_anchor = 2 +infill_anchor_max = 15 +thick_bridges = 0 +bridge_speed = 30 + [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 extrusion_width = 0.67 @@ -352,11 +415,6 @@ bottom_solid_min_thickness = 0.6 thick_bridges = 1 bridge_flow_ratio = 0.95 bridge_speed = 25 -wall_transition_angle = 10 -wall_transition_filter_deviation = 25% -wall_transition_length = 0.6 -wall_distribution_count = 1 -min_bead_width = 85% infill_overlap = 15% [print:*0.6nozzleMK3*] @@ -366,6 +424,16 @@ extrusion_width = 0.65 infill_extrusion_width = 0.65 thick_bridges = 0 +[print:*0.6nozzleXL*] +inherits = *0.6nozzle* +external_perimeter_extrusion_width = 0.65 +extrusion_width = 0.65 +infill_extrusion_width = 0.65 +thick_bridges = 0 +fill_density = 20% +support_material_interface_spacing = 0.25 +infill_anchor = 2.5 + [print:*0.6nozzleMINI*] inherits = *0.6nozzleMK3* infill_extrusion_width = 0.68 @@ -421,11 +489,19 @@ bottom_solid_min_thickness = 0.8 single_extruder_multi_material_priming = 0 thick_bridges = 1 overhangs = 0 -wall_transition_angle = 10 -wall_transition_filter_deviation = 25% -wall_transition_length = 0.8 -wall_distribution_count = 1 -min_bead_width = 85% + +[print:*0.8nozzleXL*] +inherits = *0.8nozzle* +first_layer_height = 0.2 +seam_position = nearest +infill_acceleration = 2500 +fill_pattern = rectilinear +fill_density = 15% +support_material_threshold = 45 +support_material_style = snug +raft_first_layer_expansion = 2 +default_acceleration = 1250 +infill_anchor = 2.5 [print:*soluble_support*] overhangs = 1 @@ -470,7 +546,7 @@ perimeter_speed = 30 perimeters = 3 support_material_speed = 30 top_solid_infill_speed = 20 -top_solid_layers = 15 +top_solid_layers = 14 thick_bridges = 1 [print:*0.07mm*] @@ -483,7 +559,7 @@ infill_speed = 40 solid_infill_speed = 40 support_material_speed = 40 top_solid_infill_speed = 30 -top_solid_layers = 11 +top_solid_layers = 10 [print:*0.10mm*] inherits = *common* @@ -493,10 +569,16 @@ bridge_speed = 20 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 layer_height = 0.1 perimeter_acceleration = 600 -top_solid_layers = 9 +top_solid_layers = 8 support_material_contact_distance = 0.17 raft_contact_distance = 0.15 +[print:*0.12mm*] +inherits = *0.15mm* +layer_height = 0.12 +bottom_solid_layers = 4 +top_solid_layers = 6 + [print:*0.15mm*] inherits = *common* bottom_solid_layers = 5 @@ -508,10 +590,14 @@ perimeter_acceleration = 800 perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 -top_solid_layers = 8 +top_solid_layers = 6 bridge_flow_ratio = 1 bridge_speed = 25 +[print:*0.16mm*] +inherits = *0.15mm* +layer_height = 0.16 + [print:*0.20mm*] inherits = *common* bottom_solid_layers = 4 @@ -524,7 +610,7 @@ perimeter_acceleration = 800 perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 -top_solid_layers = 6 +top_solid_layers = 5 [print:*0.25mm*] inherits = *common* @@ -551,6 +637,13 @@ top_infill_extrusion_width = 0.4 top_solid_layers = 4 support_material_contact_distance = 0.3 +[print:*0.32mm*] +inherits = *0.30mm* +bottom_solid_layers = 4 +layer_height = 0.32 +top_solid_layers = 4 +support_material_contact_distance = 0.3 + [print:*0.35mm*] inherits = *common* bottom_solid_layers = 3 @@ -1613,6 +1706,903 @@ top_solid_infill_acceleration = 800 perimeter_acceleration = 1000 external_perimeter_acceleration = 800 +## XL ## + +## XL - 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @XL 0.25] +inherits = *0.05mm*; *XL*; *0.25nozzleXL* +support_material_contact_distance = 0.07 +raft_contact_distance = 0.1 +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 40 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_speed = 40 +support_material_interface_speed = 85% +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 500 +solid_infill_acceleration = 800 +infill_acceleration = 800 +bridge_acceleration = 300 +first_layer_acceleration = 600 +default_acceleration = 800 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.25 + +[print:0.07mm ULTRADETAIL @XL 0.25] +inherits = *0.07mm*; *XL*; *0.25nozzleXL* +perimeter_speed = 30 +external_perimeter_speed = 25 +small_perimeter_speed = 20 +infill_speed = 45 +solid_infill_speed = 45 +top_solid_infill_speed = 35 +support_material_speed = 35 +support_material_interface_speed = 85% +support_material_contact_distance = 0.07 +gap_fill_speed = 25 +bridge_speed = 20 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 500 +solid_infill_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 300 +first_layer_acceleration = 600 +default_acceleration = 800 +max_print_speed = 200 +gcode_resolution = 0.006 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.25 + +[print:0.10mm QUALITY @XL 0.25] +inherits = *0.10mm*; *XL*; *0.25nozzleXL* +perimeter_speed = 35 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 30 +support_material_speed = 40 +support_material_interface_speed = 85% +support_material_contact_distance = 0.07 +gap_fill_speed = 30 +bridge_speed = 20 +external_perimeter_acceleration = 500 +perimeter_acceleration = 500 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 800 +infill_acceleration = 1200 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.25 + +[print:0.12mm QUALITY @XL 0.25] +inherits = *0.12mm*; *XL*; *0.25nozzleXL* +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 30 +support_material_speed = 50 +support_material_interface_speed = 80% +support_material_contact_distance = 0.08 +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 500 +perimeter_acceleration = 500 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 1000 +infill_acceleration = 1200 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +perimeter_extrusion_width = 0.27 +external_perimeter_extrusion_width = 0.27 +infill_extrusion_width = 0.27 +solid_infill_extrusion_width = 0.27 +top_infill_extrusion_width = 0.25 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.25 + +[print:0.15mm SPEED @XL 0.25] +inherits = *0.15mm*; *XL*; *0.25nozzleXL* +perimeter_speed = 35 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 100 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +support_material_contact_distance = 0.08 +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 500 +perimeter_acceleration = 500 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 1000 +infill_acceleration = 1200 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.3 +perimeter_extrusion_width = 0.27 +external_perimeter_extrusion_width = 0.27 +infill_extrusion_width = 0.27 +solid_infill_extrusion_width = 0.27 +top_infill_extrusion_width = 0.25 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.25 + +## XL - 0.3mm nozzle + +[print:0.05mm ULTRADETAIL @XL 0.3] +inherits = *0.05mm*; *XL*; *0.3nozzle* +top_solid_layers = 12 +bottom_solid_layers = 9 +support_material_contact_distance = 0.07 +raft_contact_distance = 0.07 +perimeter_speed = 25 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 45 +solid_infill_speed = 45 +top_solid_infill_speed = 35 +support_material_speed = 40 +support_material_interface_speed = 85% +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 800 +infill_acceleration = 800 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 800 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.3 + +[print:0.08mm DETAIL @XL 0.3] +inherits = *0.07mm*; *XL*; *0.3nozzle* +layer_height = 0.08 +support_material_contact_distance = 0.08 +raft_contact_distance = 0.08 +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 35 +support_material_speed = 40 +support_material_interface_speed = 85% +gap_fill_speed = 25 +bridge_speed = 20 +external_perimeter_acceleration = 500 +perimeter_acceleration = 600 +top_solid_infill_acceleration = 700 +solid_infill_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 600 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +perimeters = 3 +gcode_resolution = 0.006 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.3 + +[print:0.12mm QUALITY @XL 0.3] +inherits = *0.12mm*; *XL*; *0.3nozzle* +support_material_contact_distance = 0.1 +raft_contact_distance = 0.1 +perimeter_speed = 35 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 100 +solid_infill_speed = 100 +top_solid_infill_speed = 30 +support_material_speed = 45 +support_material_interface_speed = 85% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 600 +perimeter_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1200 +infill_acceleration = 1500 +bridge_acceleration = 800 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.3 + +[print:0.16mm SPEED @XL 0.3] +inherits = *0.16mm*; *XL*; *0.3nozzle* +support_material_contact_distance = 0.15 +raft_contact_distance = 0.15 +perimeter_speed = 50 +external_perimeter_speed = 30 +small_perimeter_speed = 30 +infill_speed = 120 +solid_infill_speed = 120 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 600 +perimeter_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +bridge_acceleration = 800 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.3 + +[print:0.20mm DRAFT @XL 0.3] +inherits = *0.20mm*; *XL*; *0.3nozzle* +support_material_contact_distance = 0.18 +raft_contact_distance = 0.18 +perimeter_speed = 50 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +infill_speed = 120 +solid_infill_speed = 120 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 700 +perimeter_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2500 +bridge_acceleration = 800 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.4 +perimeter_extrusion_width = 0.35 +external_perimeter_extrusion_width = 0.35 +infill_extrusion_width = 0.35 +solid_infill_extrusion_width = 0.35 +top_infill_extrusion_width = 0.3 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.3 + +## XL - 0.4mm nozzle + +[print:0.07mm ULTRADETAIL @XL 0.4] +inherits = *0.07mm*; *XL* +top_infill_extrusion_width = 0.4 +first_layer_extrusion_width = 0.45 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 +infill_extrusion_width = 0.4 +solid_infill_extrusion_width = 0.4 +perimeters = 3 +support_material_contact_distance = 0.1 +raft_contact_distance = 0.1 +perimeter_speed = 25 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 40 +top_solid_infill_speed = 30 +support_material_style = snug +support_material_interface_layers = 0 +support_material_speed = 40 +support_material_interface_speed = 85% +support_material_spacing = 1.5 +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 800 +infill_acceleration = 800 +bridge_acceleration = 300 +first_layer_acceleration = 600 +default_acceleration = 800 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 + +[print:0.10mm DETAIL @XL 0.4] +inherits = *0.10mm*; *XL* +support_material_contact_distance = 0.17 +raft_contact_distance = 0.15 +perimeter_speed = 45 +external_perimeter_speed = 30 +small_perimeter_speed = 30 +infill_speed = 90 +solid_infill_speed = 80 +top_solid_infill_speed = 35 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 40 +support_material_interface_speed = 85% +support_material_xy_spacing = 80% +gap_fill_speed = 25 +bridge_speed = 20 +external_perimeter_acceleration = 600 +perimeter_acceleration = 700 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 1500 +bridge_acceleration = 700 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 +infill_extrusion_width = 0.4 +solid_infill_extrusion_width = 0.4 +top_infill_extrusion_width = 0.4 +perimeters = 3 +gcode_resolution = 0.006 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 + +[print:0.15mm QUALITY @XL 0.4] +inherits = *0.15mm*; *XL* +perimeter_speed = 65 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 120 +solid_infill_speed = 110 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.15 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 55 +support_material_interface_speed = 70% +support_material_xy_spacing = 80% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 850 +perimeter_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2500 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +support_material_extrusion_width = 0.37 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 + +[print:0.20mm QUALITY @XL 0.4] +inherits = *0.20mm*; *XL* +perimeter_speed = 65 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 120 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 5 +support_material_xy_spacing = 80% +support_material_speed = 55 +support_material_interface_speed = 70% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 850 +perimeter_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2500 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +gcode_resolution = 0.008 +support_material_extrusion_width = 0.37 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 + +[print:0.20mm SPEED @XL 0.4] +inherits = *0.20mm*; *XL* +perimeter_speed = 90 +external_perimeter_speed = 70 +small_perimeter_speed = 40 +infill_speed = 200 +solid_infill_speed = 140 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 60 +support_material_interface_speed = 70% +support_material_xy_spacing = 80% +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1200 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 2000 +infill_acceleration = 3000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +support_material_extrusion_width = 0.37 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 + +[print:0.30mm DRAFT @XL 0.4] +inherits = *0.30mm*; *XL* +bottom_solid_layers = 3 +perimeter_speed = 80 +external_perimeter_speed = 70 +small_perimeter_speed = 40 +infill_speed = 200 +solid_infill_speed = 200 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 60 +support_material_interface_speed = 70% +support_material_xy_spacing = 80% +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1200 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 2000 +infill_acceleration = 3000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +external_perimeter_extrusion_width = 0.55 +extrusion_width = 0.55 +first_layer_extrusion_width = 0.5 +infill_extrusion_width = 0.55 +perimeter_extrusion_width = 0.55 +solid_infill_extrusion_width = 0.55 +top_infill_extrusion_width = 0.42 +support_material_extrusion_width = 0.38 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 + +## XL - 0.5mm nozzle + +[print:0.10mm DETAIL @XL 0.5] +inherits = *0.10mm*; *XL*; *0.5nozzle* +perimeter_speed = 40 +external_perimeter_speed = 30 +small_perimeter_speed = 25 +infill_speed = 90 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 85% +support_material_style = snug +support_material_interface_layers = 4 +gap_fill_speed = 40 +bridge_speed = 30 +external_perimeter_acceleration = 700 +perimeter_acceleration = 800 +infill_acceleration = 2000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +perimeter_extrusion_width = 0.5 +external_perimeter_extrusion_width = 0.5 +infill_extrusion_width = 0.5 +solid_infill_extrusion_width = 0.5 +top_infill_extrusion_width = 0.45 +perimeters = 2 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.5 + +[print:0.15mm QUALITY @XL 0.5] +inherits = *0.15mm*; *XL*; *0.5nozzle* +perimeter_speed = 65 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 120 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 70% +support_material_style = snug +support_material_interface_layers = 4 +gap_fill_speed = 40 +bridge_speed = 30 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +infill_acceleration = 2000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.5 + +[print:0.20mm QUALITY @XL 0.5] +inherits = 0.15mm QUALITY @XL 0.5; *0.20mm*; *XL*; *0.5nozzle* +gcode_resolution = 0.01 +support_material_interface_layers = 4 +infill_speed = 200 +solid_infill_speed = 120 +support_material_speed = 60 +support_material_interface_speed = 70% +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +infill_acceleration = 2500 +default_acceleration = 1250 +max_print_speed = 200 + +[print:0.25mm SPEED @XL 0.5] +inherits = *0.25mm*; *XL*; *0.5nozzle* +bottom_solid_layers = 3 +perimeter_speed = 70 +external_perimeter_speed = 70 +small_perimeter_speed = 45 +infill_speed = 200 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +support_material_style = snug +support_material_interface_layers = 4 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 900 +perimeter_acceleration = 1000 +infill_acceleration = 2500 +bridge_acceleration = 800 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.5 + +[print:0.32mm DRAFT @XL 0.5] +inherits = *0.32mm*; *XL*; *0.5nozzle* +bottom_solid_layers = 3 +perimeter_speed = 70 +external_perimeter_speed = 70 +small_perimeter_speed = 45 +infill_speed = 200 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +support_material_style = snug +support_material_interface_layers = 4 +support_material_contact_distance = 0.3 +support_material_extrusion_width = 0.42 +raft_contact_distance = 0.3 +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1000 +infill_acceleration = 2500 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +max_print_speed = 200 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.5 + +## XL - 0.6mm nozzle + +[print:0.15mm DETAIL @XL 0.6] +inherits = *0.15mm*; *XL*; *0.6nozzleXL* +perimeter_speed = 45 +external_perimeter_speed = 30 +small_perimeter_speed = 30 +infill_speed = 100 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.22 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.65 +external_perimeter_extrusion_width = 0.6 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.6 +perimeter_extrusion_width = 0.6 +solid_infill_extrusion_width = 0.6 +top_infill_extrusion_width = 0.5 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 800 +infill_acceleration = 2000 +bridge_acceleration = 800 +first_layer_acceleration = 600 +default_acceleration = 1250 +bridge_flow_ratio = 1 +max_print_speed = 200 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[print:0.20mm DETAIL @XL 0.6] +inherits = *0.20mm*; *XL*; *0.6nozzleXL* +perimeter_speed = 50 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +infill_speed = 120 +solid_infill_speed = 100 +support_material_contact_distance = 0.22 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 4 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.65 +external_perimeter_extrusion_width = 0.6 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.6 +perimeter_extrusion_width = 0.6 +solid_infill_extrusion_width = 0.6 +top_infill_extrusion_width = 0.5 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 900 +infill_acceleration = 2500 +first_layer_acceleration = 600 +default_acceleration = 1250 +bridge_flow_ratio = 1 +max_print_speed = 200 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[print:0.25mm QUALITY @XL 0.6] +inherits = *0.25mm*; *XL*; *0.6nozzleXL* +perimeter_speed = 65 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 75% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.65 +top_infill_extrusion_width = 0.55 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +infill_acceleration = 2500 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1250 +bridge_flow_ratio = 1 +top_solid_layers = 5 +bottom_solid_layers = 4 +max_print_speed = 200 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[print:0.25mm SPEED @XL 0.6] +inherits = 0.25mm QUALITY @XL 0.6 +perimeter_speed = 75 +external_perimeter_speed = 65 +small_perimeter_speed = 40 +solid_infill_speed = 100 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 55 +support_material_interface_speed = 75% +gap_fill_speed = 50 +top_infill_extrusion_width = 0.6 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1200 +solid_infill_acceleration = 2000 +infill_acceleration = 3000 +top_solid_layers = 4 +bottom_solid_layers = 4 +default_acceleration = 1250 +dynamic_overhang_speeds = 35,20,15,15 + +[print:0.32mm QUALITY @XL 0.6] +inherits = *0.32mm*; *XL*; *0.6nozzleXL* +perimeter_speed = 65 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 75% +gap_fill_speed = 50 +bridge_speed = 25 +extrusion_width = 0.68 +external_perimeter_extrusion_width = 0.68 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +top_infill_extrusion_width = 0.55 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +solid_infill_acceleration = 1500 +infill_acceleration = 2500 +first_layer_acceleration = 600 +default_acceleration = 1250 +bridge_flow_ratio = 0.95 +max_print_speed = 200 +bottom_solid_layers = 3 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[print:0.32mm SPEED @XL 0.6] +inherits = *0.32mm*; *XL*; *0.6nozzleXL* +perimeter_speed = 70 +external_perimeter_speed = 65 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 55 +support_material_interface_speed = 75% +gap_fill_speed = 50 +bridge_speed = 25 +extrusion_width = 0.68 +external_perimeter_extrusion_width = 0.68 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1200 +solid_infill_acceleration = 2000 +infill_acceleration = 3000 +first_layer_acceleration = 600 +default_acceleration = 1250 +bridge_flow_ratio = 0.95 +max_print_speed = 200 +bottom_solid_layers = 3 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[print:0.40mm DRAFT @XL 0.6] +inherits = *0.40mm*; *XL*; *0.6nozzleXL* +perimeter_speed = 60 +external_perimeter_speed = 45 +small_perimeter_speed = 40 +infill_speed = 200 +solid_infill_speed = 55 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.68 +external_perimeter_extrusion_width = 0.68 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1200 +solid_infill_acceleration = 2000 +infill_acceleration = 3000 +default_acceleration = 1500 +bridge_flow_ratio = 0.95 +dynamic_overhang_speeds = 30,20,15,15 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +## XL - 0.8mm nozzle + +[print:0.30mm DETAIL @XL 0.8] +inherits = *0.30mm*; *XL*; *0.8nozzleXL* +perimeter_speed = 45 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 100 +solid_infill_speed = 50 +support_material_speed = 40 +support_material_interface_speed = 100% +top_solid_infill_speed = 35 +bridge_speed = 22 +gap_fill_speed = 30 +top_infill_extrusion_width = 0.75 +support_material_extrusion_width = 0.7 +external_perimeter_acceleration = 900 +perimeter_acceleration = 1000 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 + +[print:0.40mm QUALITY @XL 0.8] +inherits = *0.40mm*; *XL*; *0.8nozzleXL* +perimeter_speed = 45 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 70 +solid_infill_speed = 45 +top_solid_infill_speed = 35 +support_material_speed = 40 +support_material_interface_speed = 100% +bridge_speed = 22 +gap_fill_speed = 30 +top_infill_extrusion_width = 0.8 +support_material_extrusion_width = 0.7 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1000 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 2000 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 + +[print:0.55mm DRAFT @XL 0.8] +inherits = *XL*; *0.8nozzleXL* +layer_height = 0.55 +top_solid_layers = 4 +bottom_solid_layers = 3 +perimeter_speed = 40 +external_perimeter_speed = 35 +small_perimeter_speed = 35 +infill_speed = 70 +solid_infill_speed = 35 +top_solid_infill_speed = 35 +support_material_speed = 35 +support_material_interface_speed = 100% +bridge_speed = 22 +gap_fill_speed = 30 +top_infill_extrusion_width = 0.8 +support_material_extrusion_width = 0.7 +perimeter_extrusion_width = 1 +external_perimeter_extrusion_width = 1 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1000 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 2000 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 + # XXXXXXxxXXXXXXXXXXXXXX # XXX--- filament ---XXX # XXXXXXXXxxXXXXXXXXXXXX @@ -1621,7 +2611,7 @@ external_perimeter_acceleration = 800 cooling = 1 compatible_printers = # For now, all but selected filaments are disabled for the MMU 2.0 -compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = ! single_extruder_multi_material and printer_model!="XL" end_filament_gcode = "; Filament-specific end gcode" extrusion_multiplier = 1 filament_loading_speed = 28 @@ -1664,6 +2654,36 @@ min_fan_speed = 100 temperature = 210 slowdown_below_layer_time = 10 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +compatible_printers_condition = ! single_extruder_multi_material and printer_model!="XL" + +[filament:*PLAPG*] +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.06{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.08{elsif nozzle_diameter[0]==0.35}0.07{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +slowdown_below_layer_time = 8 +filament_cooling_final_speed = 2 +filament_cooling_initial_speed = 3 +filament_cooling_moves = 1 +filament_load_time = 15 +filament_loading_speed = 14 +filament_ramming_parameters = "130 120 2.70968 2.93548 3.32258 3.83871 4.58065 5.54839 6.51613 7.35484 7.93548 8.16129| 0.05 2.66451 0.45 3.05805 0.95 4.05807 1.45 5.97742 1.95 7.69999 2.45 8.1936 2.95 11.342 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6" +filament_unload_time = 12 +filament_unloading_speed = 20 +filament_loading_speed_start = 19 +filament_minimal_purge_on_wipe_tower = 15 +filament_unloading_speed_start = 100 +## idle_temperature = 170 + +[filament:*PLA06PG*] +inherits = *PLAPG* +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 +filament_max_volumetric_speed = 15.5 +slowdown_below_layer_time = 10 + +[filament:*PLA08PG*] +inherits = *PLAPG* +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +filament_max_volumetric_speed = 19 +slowdown_below_layer_time = 18 [filament:*PET*] inherits = *common* @@ -1684,13 +2704,51 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 240 filament_retract_length = 1 filament_retract_lift = 0.2 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:*PET06*] inherits = *PET* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) filament_max_volumetric_speed = 15 +[filament:*PETPG*] +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +filament_max_volumetric_speed = 10 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.08{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.1{elsif nozzle_diameter[0]==0.35}0.09{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp" +filament_cooling_final_speed = 1 +filament_cooling_initial_speed = 2 +filament_cooling_moves = 1 +filament_load_time = 15 +filament_loading_speed = 14 +filament_ramming_parameters = "120 140 4.70968 4.74194 4.77419 4.80645 4.83871 4.87097 4.90323 5 5.25806 5.67742 6.29032 7.06452 7.83871 8.3871| 0.05 4.72901 0.45 4.73545 0.95 4.83226 1.45 4.88067 1.95 5.05483 2.45 5.93553 2.95 7.53556 3.45 8.6323 3.95 7.6 4.45 7.6 4.95 7.6" +filament_unload_time = 12 +filament_unloading_speed = 20 +filament_unloading_speed_start = 120 +filament_loading_speed_start = 19 +## idle_temperature = 170 +filament_retract_length = 1 +filament_retract_lift = 0.15 +filament_retract_before_wipe = 0 +slowdown_below_layer_time = 9 + +[filament:*PET06PG*] +inherits = *PETPG* +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 +filament_max_volumetric_speed = 17 +slowdown_below_layer_time = 12 + +[filament:*PET08PG*] +inherits = *PETPG* +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +filament_max_volumetric_speed = 22 +slowdown_below_layer_time = 18 + +[filament:*04PLUS*] +compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! single_extruder_multi_material and printer_model!="XL" + +[filament:*04PLUSPG*] +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL" + [filament:*PETMMU1*] # inherits = *PET* filament_retract_length = nil @@ -1776,7 +2834,7 @@ max_fan_speed = 30 min_fan_speed = 20 temperature = 255 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*ABSC*] inherits = *common* @@ -1798,14 +2856,75 @@ min_fan_speed = 15 min_print_speed = 15 temperature = 255 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:*ABSPG*] +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +filament_max_volumetric_speed = 14 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.04{elsif nozzle_diameter[0]==0.25}0.1{elsif nozzle_diameter[0]==0.3}0.06{elsif nozzle_diameter[0]==0.35}0.05{elsif nozzle_diameter[0]==0.5}0.03{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.01{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp" +filament_cooling_final_speed = 50 +filament_cooling_initial_speed = 10 +filament_cooling_moves = 5 +filament_ramming_parameters = "120 110 5.32258 5.45161 5.67742 6 6.48387 7.12903 7.90323 8.70968 9.3871 9.83871 10.0968 10.2258| 0.05 5.30967 0.45 5.50967 0.95 6.1871 1.45 7.39677 1.95 9.05484 2.45 10 2.95 10.3098 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6"; +filament_loading_speed_start = 19 +filament_load_time = 15 +filament_unload_time = 12 +filament_loading_speed = 14 +filament_unloading_speed = 20 +## idle_temperature = 170 + +[filament:*ABS06PG*] +inherits = *ABSPG* +filament_max_volumetric_speed = 15 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[filament:*ABS08PG*] +inherits = *ABSPG* +filament_max_volumetric_speed = 18 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +slowdown_below_layer_time = 25 + +[filament:*PCPG*] +inherits = *ABSPG* +filament_max_volumetric_speed = 8 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S45 ; set heatbreak target temp\n" +first_layer_bed_temperature = 100 +bed_temperature = 105 +## idle_temperature = 170 + +[filament:*PC06PG*] +inherits = *PCPG* +filament_max_volumetric_speed = 14 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[filament:*PC08PG*] +inherits = *PCPG* +filament_max_volumetric_speed = 20 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 + +[filament:*PAPG*] +inherits = *ABSPG* +filament_max_volumetric_speed = 5 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S45 ; set heatbreak target temp\n" +bed_temperature = 105 +## idle_temperature = 170 + +[filament:*PA06PG*] +inherits = *PCPG* +filament_max_volumetric_speed = 7 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[filament:*PA08PG*] +inherits = *PCPG* +filament_max_volumetric_speed = 10 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 [filament:*FLEX*] inherits = *common* bed_temperature = 50 bridge_fan_speed = 80 # For now, all but selected filaments are disabled for the MMU 2.0 -compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) cooling = 0 disable_fan_first_layers = 3 extrusion_multiplier = 1.15 @@ -1818,17 +2937,35 @@ first_layer_bed_temperature = 50 first_layer_temperature = 240 max_fan_speed = 90 min_fan_speed = 70 -start_filament_gcode = "M900 K0"; Filament gcode" +start_filament_gcode = "M900 K0 ; Filament gcode" temperature = 240 filament_retract_length = 0.8 filament_deretract_speed = 25 filament_retract_lift = 0 filament_wipe = 0 +[filament:*FLEXPG*] +filament_max_volumetric_speed = 4 +filament_retract_speed = 60 +filament_deretract_speed = 20 +filament_retract_before_travel = 2 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +## idle_temperature = 170 +start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" + +[filament:*FLEX06PG*] +inherits = *FLEXPG* +filament_max_volumetric_speed = 6.5 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 + +[filament:*FLEX08PG*] +inherits = *FLEXPG* +filament_max_volumetric_speed = 9 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 + [filament:ColorFabb bronzeFill] -inherits = *PLA* +inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) extrusion_multiplier = 1.12 filament_cost = 80.65 filament_density = 3.9 @@ -1836,27 +2973,59 @@ filament_spool_weight = 236 filament_colour = #804040 filament_max_volumetric_speed = 9 +[filament:ColorFabb bronzeFill @PG] +inherits = ColorFabb bronzeFill; *PLAPG*; *04PLUSPG* +filament_max_volumetric_speed = 9 + +[filament:ColorFabb bronzeFill @PG 0.6] +inherits = ColorFabb bronzeFill; *PLA06PG* +filament_max_volumetric_speed = 14 +extrusion_multiplier = 1.05 + +[filament:ColorFabb bronzeFill @PG 0.8] +inherits = ColorFabb bronzeFill; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1.05 + [filament:ColorFabb steelFill] -inherits = *PLA* -filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +inherits = ColorFabb bronzeFill extrusion_multiplier = 1.15 -filament_cost = 80.65 filament_density = 3.13 -filament_spool_weight = 236 filament_colour = #808080 filament_max_volumetric_speed = 8 +[filament:ColorFabb steelFill @PG] +inherits = ColorFabb steelFill; *PLAPG*; *04PLUSPG* +filament_max_volumetric_speed = 8 + +[filament:ColorFabb steelFill @PG 0.6] +inherits = ColorFabb steelFill; *PLA06PG* +filament_max_volumetric_speed = 14 +extrusion_multiplier = 1.05 + +[filament:ColorFabb steelFill @PG 0.8] +inherits = ColorFabb steelFill; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1.05 + [filament:ColorFabb copperFill] -inherits = *PLA* -filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +inherits = ColorFabb bronzeFill extrusion_multiplier = 1.15 -filament_cost = 80.65 filament_density = 3.9 -filament_spool_weight = 236 filament_colour = #82603E -filament_max_volumetric_speed = 9 + +[filament:ColorFabb copperFill @PG] +inherits = ColorFabb copperFill; *PLAPG*; *04PLUSPG* + +[filament:ColorFabb copperFill @PG 0.6] +inherits = ColorFabb copperFill; *PLA06PG* +filament_max_volumetric_speed = 14 +extrusion_multiplier = 1.05 + +[filament:ColorFabb copperFill @PG 0.8] +inherits = ColorFabb copperFill; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1.05 [filament:ColorFabb HT] inherits = *PET* @@ -1876,6 +3045,17 @@ max_fan_speed = 20 min_fan_speed = 10 temperature = 270 +[filament:ColorFabb HT @PG] +inherits = ColorFabb HT; *PETPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:ColorFabb HT @PG 0.6] +inherits = ColorFabb HT @PG; *PET06PG* + +[filament:ColorFabb HT @PG 0.8] +inherits = ColorFabb HT @PG; *PET08PG* + [filament:ColorFabb PLA-PHA] inherits = *PLA* filament_vendor = ColorFabb @@ -1883,10 +3063,18 @@ filament_cost = 54.84 filament_density = 1.24 filament_spool_weight = 236 +[filament:ColorFabb PLA-PHA @PG] +inherits = ColorFabb PLA-PHA; *PLAPG* + +[filament:ColorFabb PLA-PHA @PG 0.6] +inherits = ColorFabb PLA-PHA; *PLA06PG* + +[filament:ColorFabb PLA-PHA @PG 0.8] +inherits = ColorFabb PLA-PHA; *PLA08PG* + [filament:ColorFabb woodFill] -inherits = *PLA* +inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) extrusion_multiplier = 1.1 filament_cost = 78.63 filament_density = 1.15 @@ -1898,10 +3086,22 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 200 filament_retract_lift = 0.2 +[filament:ColorFabb woodFill @PG] +inherits = ColorFabb woodFill; *PLAPG*; *04PLUSPG* + +[filament:ColorFabb woodFill @PG 0.6] +inherits = ColorFabb woodFill; *PLA06PG* +filament_max_volumetric_speed = 14 +extrusion_multiplier = 1 + +[filament:ColorFabb woodFill @PG 0.8] +inherits = ColorFabb woodFill; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1 + [filament:ColorFabb corkFill] -inherits = *PLA* +inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) extrusion_multiplier = 1.1 filament_cost = 78.63 filament_density = 1.18 @@ -1913,6 +3113,19 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 220 filament_retract_lift = 0.2 +[filament:ColorFabb corkFill @PG] +inherits = ColorFabb corkFill; *PLAPG*; *04PLUSPG* + +[filament:ColorFabb corkFill @PG 0.6] +inherits = ColorFabb corkFill; *PLA06PG* +filament_max_volumetric_speed = 14 +extrusion_multiplier = 1.05 + +[filament:ColorFabb corkFill @PG 0.8] +inherits = ColorFabb corkFill; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1.05 + [filament:ColorFabb XT] inherits = *PET* filament_vendor = ColorFabb @@ -1923,6 +3136,15 @@ first_layer_bed_temperature = 90 first_layer_temperature = 260 temperature = 270 +[filament:ColorFabb XT @PG] +inherits = ColorFabb XT; *PETPG* + +[filament:ColorFabb XT @PG 0.6] +inherits = ColorFabb XT; *PET06PG* + +[filament:ColorFabb XT @PG 0.8] +inherits = ColorFabb XT; *PET08PG* + [filament:ColorFabb XT-CF20] inherits = *PET* filament_vendor = ColorFabb @@ -1938,7 +3160,19 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 260 filament_retract_length = nil filament_retract_lift = 0.4 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:ColorFabb XT-CF20 @PG] +inherits = ColorFabb XT-CF20; *PETPG*; *04PLUSPG* +filament_max_volumetric_speed = 3 + +[filament:ColorFabb XT-CF20 @PG 0.6] +inherits = ColorFabb XT-CF20 @PG; *PET06PG* +filament_max_volumetric_speed = 6 + +[filament:ColorFabb XT-CF20 @PG 0.8] +inherits = ColorFabb XT-CF20 @PG; *PET08PG* +filament_max_volumetric_speed = 9 [filament:ColorFabb nGen] inherits = *PET* @@ -1954,6 +3188,15 @@ first_layer_temperature = 240 max_fan_speed = 35 min_fan_speed = 20 +[filament:ColorFabb nGen @PG] +inherits = ColorFabb nGen; *PETPG* + +[filament:ColorFabb nGen @PG 0.6] +inherits = ColorFabb nGen; *PET06PG* + +[filament:ColorFabb nGen @PG 0.8] +inherits = ColorFabb nGen; *PET08PG* + [filament:ColorFabb nGen flex] inherits = *FLEX* filament_vendor = ColorFabb @@ -1974,7 +3217,20 @@ min_fan_speed = 20 temperature = 260 filament_retract_length = nil filament_retract_lift = 0 -compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and printer_model!="XL" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) + +[filament:ColorFabb nGen flex @PG] +inherits = ColorFabb nGen flex; *FLEXPG* +filament_max_volumetric_speed = 7 +filament_retract_length = 2.5 + +[filament:ColorFabb nGen flex @PG 0.6] +inherits = ColorFabb nGen flex; *FLEX06PG* +filament_max_volumetric_speed = 9 + +[filament:ColorFabb nGen flex @PG 0.8] +inherits = ColorFabb nGen flex; *FLEX08PG* +filament_max_volumetric_speed = 12 [filament:Kimya PETG Carbon] inherits = *PET* @@ -1990,7 +3246,18 @@ temperature = 240 filament_retract_length = nil filament_retract_lift = 0.3 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.06{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Kimya PETG Carbon @PG] +inherits = Kimya PETG Carbon; *PETPG*; *04PLUSPG* + +[filament:Kimya PETG Carbon @PG 0.6] +inherits = Kimya PETG Carbon @PG; *PET06PG* +filament_max_volumetric_speed = 9 + +[filament:Kimya PETG Carbon @PG 0.8] +inherits = Kimya PETG Carbon @PG; *PET08PG* +filament_max_volumetric_speed = 14 [filament:Kimya ABS Carbon] inherits = *ABSC* @@ -2001,22 +3268,82 @@ filament_colour = #804040 filament_max_volumetric_speed = 6 first_layer_temperature = 260 temperature = 260 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Kimya ABS Carbon @PG] +inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG* +bed_temperature = 105 +filament_max_volumetric_speed = 6 + +[filament:Kimya ABS Carbon @PG 0.6] +inherits = Kimya ABS Carbon @PG; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Kimya ABS Carbon @PG 0.8] +inherits = Kimya ABS Carbon @PG; *ABS08PG* +filament_max_volumetric_speed = 14 [filament:Kimya ABS Kevlar] inherits = Kimya ABS Carbon filament_vendor = Kimya filament_density = 1.037 +[filament:Kimya ABS Kevlar @PG] +inherits = Kimya ABS Kevlar; *ABSPG*; *04PLUSPG* +bed_temperature = 105 + +[filament:Kimya ABS Kevlar @PG 0.6] +inherits = Kimya ABS Kevlar @PG; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Kimya ABS Kevlar @PG 0.8] +inherits = Kimya ABS Kevlar @PG; *ABS08PG* +filament_max_volumetric_speed = 14 + +[filament:Kimya PEBA-S] +inherits = *PET* +filament_vendor = Kimya +first_layer_temperature = 250 +temperature = 250 +filament_cost = 125.84 +filament_density = 1.013 +filament_spool_weight = 0 +filament_max_volumetric_speed = 6.5 +filament_type = PEBA +min_fan_speed = 30 +max_fan_speed = 30 +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Kimya PEBA-S @PG] +inherits = Kimya PEBA-S; *PETPG* +filament_max_volumetric_speed = 6.5 + +[filament:Kimya PEBA-S @PG 0.6] +inherits = Kimya PEBA-S @PG; *PET06PG* +filament_max_volumetric_speed = 8 + +[filament:Kimya PEBA-S @PG 0.8] +inherits = Kimya PEBA-S @PG; *PET08PG* +filament_max_volumetric_speed = 10 + [filament:E3D Edge] inherits = *PET* filament_vendor = E3D filament_cost = 56.9 filament_density = 1.26 filament_type = EDGE -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:E3D Edge @PG] +inherits = E3D Edge; *PETPG* + +[filament:E3D Edge @PG 0.6] +inherits = E3D Edge; *PET06PG* + +[filament:E3D Edge @PG 0.8] +inherits = E3D Edge; *PET08PG* [filament:E3D PC-ABS] +## discontinued inherits = *ABS* filament_vendor = E3D filament_cost = 0 @@ -2032,6 +3359,15 @@ filament_cost = 35.48 filament_density = 1.24 filament_spool_weight = 230 +[filament:Fillamentum PLA @PG] +inherits = Fillamentum PLA; *PLAPG* + +[filament:Fillamentum PLA @PG 0.6] +inherits = Fillamentum PLA; *PLA06PG* + +[filament:Fillamentum PLA @PG 0.8] +inherits = Fillamentum PLA; *PLA08PG* + [filament:Fillamentum ABS] inherits = *ABSC* filament_vendor = Fillamentum @@ -2041,6 +3377,16 @@ filament_spool_weight = 230 first_layer_temperature = 240 temperature = 240 +[filament:Fillamentum ABS @PG] +inherits = Fillamentum ABS; *ABSPG* +bed_temperature = 105 + +[filament:Fillamentum ABS @PG 0.6] +inherits = Fillamentum ABS @PG; *ABS06PG* + +[filament:Fillamentum ABS @PG 0.8] +inherits = Fillamentum ABS @PG; *ABS08PG* + [filament:Fillamentum ASA] inherits = *ABS* filament_vendor = Fillamentum @@ -2057,6 +3403,16 @@ first_layer_temperature = 260 temperature = 260 filament_type = ASA +[filament:Fillamentum ASA @PG] +inherits = Fillamentum ASA; *ABSPG* +bed_temperature = 105 + +[filament:Fillamentum ASA @PG 0.6] +inherits = Fillamentum ASA @PG; *ABS06PG* + +[filament:Fillamentum ASA @PG 0.8] +inherits = Fillamentum ASA @PG; *ABS08PG* + [filament:Prusament ASA] inherits = *ABS* filament_vendor = Prusa Polymers @@ -2078,7 +3434,20 @@ disable_fan_first_layers = 4 filament_type = ASA filament_colour = #FFF2EC start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Prusament ASA @PG] +inherits = Prusament ASA; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:Prusament ASA @PG 0.6] +inherits = Prusament ASA @PG; *ABS06PG* + +[filament:Prusament ASA @PG 0.8] +inherits = Prusament ASA @PG; *ABS08PG* +first_layer_temperature = 265 +temperature = 265 [filament:Prusament PC Blend] inherits = *ABS* @@ -2103,15 +3472,27 @@ filament_type = PC filament_colour = #DEE0E6 filament_max_volumetric_speed = 8 filament_retract_lift = 0.2 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Prusament PC Blend @PG] +inherits = Prusament PC Blend; *PCPG* +filament_max_volumetric_speed = 9 + +[filament:Prusament PC Blend @PG 0.6] +inherits = Prusament PC Blend @PG; *PC06PG* +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend @PG 0.8] +inherits = Prusament PC Blend @PG; *PC08PG* +filament_max_volumetric_speed = 18 + [filament:Prusament PC Blend @MK2] inherits = Prusament PC Blend first_layer_bed_temperature = 105 bed_temperature = 110 disable_fan_first_layers = 6 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! single_extruder_multi_material [filament:Prusament PC Blend Carbon Fiber] inherits = Prusament PC Blend @@ -2125,7 +3506,18 @@ fan_below_layer_time = 10 filament_colour = #BBBBBB filament_retract_length = nil filament_retract_lift = nil -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusament PC Blend Carbon Fiber @PG] +inherits = Prusament PC Blend Carbon Fiber; *PCPG* + +[filament:Prusament PC Blend Carbon Fiber @PG 0.6] +inherits = Prusament PC Blend Carbon Fiber; *PC06PG* +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend Carbon Fiber @PG 0.8] +inherits = Prusament PC Blend Carbon Fiber; *PC08PG* +filament_max_volumetric_speed = 18 [filament:Prusament PC Blend Carbon Fiber @MK2] inherits = Prusament PC Blend Carbon Fiber @@ -2147,7 +3539,19 @@ temperature = 285 first_layer_bed_temperature = 90 bed_temperature = 115 fan_below_layer_time = 10 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusament PA11 Carbon Fiber @PG] +inherits = Prusament PA11 Carbon Fiber; *PCPG* +filament_max_volumetric_speed = 6.5 + +[filament:Prusament PA11 Carbon Fiber @PG 0.6] +inherits = Prusament PA11 Carbon Fiber @PG; *PC06PG* +filament_max_volumetric_speed = 8 + +[filament:Prusament PA11 Carbon Fiber @PG 0.8] +inherits = Prusament PA11 Carbon Fiber @PG; *PC08PG* +filament_max_volumetric_speed = 10 [filament:Prusament PA11 Carbon Fiber @MK2] inherits = Prusament PA11 Carbon Fiber @@ -2173,10 +3577,18 @@ full_fan_speed_layer = 5 temperature = 275 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Fillamentum CPE @PG] +inherits = Fillamentum CPE; *PETPG* + +[filament:Fillamentum CPE @PG 0.6] +inherits = Fillamentum CPE; *PET06PG* + +[filament:Fillamentum CPE @PG 0.8] +inherits = Fillamentum CPE; *PET08PG* + [filament:Fillamentum Timberfill] -inherits = *PLA* +inherits = *PLA*; *04PLUS* filament_vendor = Fillamentum -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) extrusion_multiplier = 1.1 filament_cost = 68 filament_density = 1.15 @@ -2188,10 +3600,23 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 190 filament_retract_lift = 0.2 +[filament:Fillamentum Timberfill @PG] +inherits = Fillamentum Timberfill; *PLAPG*; *04PLUSPG* +filament_max_volumetric_speed = 11 + +[filament:Fillamentum Timberfill @PG 0.6] +inherits = Fillamentum Timberfill; *PLA06PG* +filament_max_volumetric_speed = 13 +extrusion_multiplier = 1.05 + +[filament:Fillamentum Timberfill @PG 0.8] +inherits = Fillamentum Timberfill; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1.05 + [filament:Smartfil Wood] -inherits = *PLA* +inherits = *PLA*; *04PLUS* filament_vendor = Smart Materials 3D -compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) extrusion_multiplier = 1.1 filament_cost = 68 filament_density = 1.58 @@ -2202,12 +3627,38 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 220 filament_retract_lift = 0.2 +[filament:Smartfil Wood @PG] +inherits = Smartfil Wood; *PLAPG* +filament_max_volumetric_speed = 11 + +[filament:Smartfil Wood @PG 0.6] +inherits = Smartfil Wood; *PLA06PG* +filament_max_volumetric_speed = 13 +extrusion_multiplier = 1.05 + +[filament:Smartfil Wood @PG 0.8] +inherits = Smartfil Wood; *PLA08PG* +filament_max_volumetric_speed = 17 +extrusion_multiplier = 1.05 + [filament:Generic ABS] inherits = *ABSC* filament_vendor = Generic filament_cost = 27.82 filament_density = 1.04 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Generic ABS @PG] +inherits = Generic ABS; *ABSPG* +bed_temperature = 105 + +[filament:Generic ABS @PG 0.6] +inherits = Generic ABS @PG; *ABS06PG* + +[filament:Generic ABS @PG 0.8] +inherits = Generic ABS @PG; *ABS08PG* +first_layer_temperature = 265 +temperature = 265 [filament:Esun ABS] inherits = *ABSC* @@ -2216,6 +3667,16 @@ filament_cost = 27.82 filament_density = 1.01 filament_spool_weight = 265 +[filament:Esun ABS @PG] +inherits = Esun ABS; *ABSPG* +bed_temperature = 105 + +[filament:Esun ABS @PG 0.6] +inherits = Esun ABS @PG; *ABS06PG* + +[filament:Esun ABS @PG 0.8] +inherits = Esun ABS @PG; *ABS08PG* + [filament:Hatchbox ABS] inherits = *ABSC* filament_vendor = Hatchbox @@ -2223,6 +3684,16 @@ filament_cost = 27.82 filament_density = 1.04 filament_spool_weight = 245 +[filament:Hatchbox ABS @PG] +inherits = Hatchbox ABS; *ABSPG* +bed_temperature = 105 + +[filament:Hatchbox ABS @PG 0.6] +inherits = Hatchbox ABS @PG; *ABS06PG* + +[filament:Hatchbox ABS @PG 0.8] +inherits = Hatchbox ABS @PG; *ABS08PG* + [filament:Filament PM ABS] inherits = *ABSC* renamed_from = "Plasty Mladec ABS" @@ -2231,6 +3702,16 @@ filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 +[filament:Filament PM ABS @PG] +inherits = Filament PM ABS; *ABSPG* +bed_temperature = 105 + +[filament:Filament PM ABS @PG 0.6] +inherits = Filament PM ABS @PG; *ABS06PG* + +[filament:Filament PM ABS @PG 0.8] +inherits = Filament PM ABS @PG; *ABS08PG* + [filament:Verbatim ABS] inherits = *ABSC* filament_vendor = Verbatim @@ -2239,13 +3720,35 @@ filament_density = 1.05 filament_spool_weight = 235 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" +[filament:Verbatim ABS @PG] +inherits = Verbatim ABS; *ABSPG* +bed_temperature = 105 + +[filament:Verbatim ABS @PG 0.6] +inherits = Verbatim ABS @PG; *ABS06PG* + +[filament:Verbatim ABS @PG 0.8] +inherits = Verbatim ABS @PG; *ABS08PG* + [filament:Generic PETG] inherits = *PET* renamed_from = "Generic PET" filament_vendor = Generic filament_cost = 27.82 filament_density = 1.27 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Generic PETG @PG] +inherits = Generic PETG; *PETPG* + +[filament:Generic PETG @PG 0.6] +inherits = Generic PETG; *PET06PG* +filament_max_volumetric_speed = 17 + +[filament:Generic PETG @PG 0.8] +inherits = Generic PETG; *PET08PG* +first_layer_temperature = 240 +temperature = 250 [filament:Extrudr DuraPro ASA] inherits = Fillamentum ASA @@ -2258,9 +3761,18 @@ first_layer_bed_temperature = 90 first_layer_temperature = 220 temperature = 220 filament_max_volumetric_speed = 10 -compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) filament_spool_weight = 230 +[filament:Extrudr DuraPro ASA @PG] +inherits = Extrudr DuraPro ASA; *ABSPG* +filament_max_volumetric_speed = 10 + +[filament:Extrudr DuraPro ASA @PG 0.6] +inherits = Extrudr DuraPro ASA @PG; *ABS06PG* + +[filament:Extrudr DuraPro ASA @PG 0.8] +inherits = Extrudr DuraPro ASA @PG; *ABS08PG* + [filament:Extrudr PETG] inherits = *PET* filament_vendor = Extrudr @@ -2276,7 +3788,15 @@ filament_retract_length = nil filament_retract_lift = nil filament_spool_weight = 262 full_fan_speed_layer = 0 -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Extrudr PETG @PG] +inherits = Extrudr PETG; *PETPG* + +[filament:Extrudr PETG @PG 0.6] +inherits = Extrudr PETG; *PET06PG* + +[filament:Extrudr PETG @PG 0.8] +inherits = Extrudr PETG; *PET08PG* [filament:Extrudr PETG @MINI] inherits = Extrudr PETG; *PETMINI* @@ -2289,9 +3809,18 @@ filament_density = 1.29 filament_notes = "https://www.extrudr.com/en/products/catalogue/?material=198" first_layer_temperature = 235 temperature = 235 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) filament_spool_weight = 230 +[filament:Extrudr XPETG CF @PG] +inherits = Extrudr XPETG CF; *PETPG* + +[filament:Extrudr XPETG CF @PG 0.6] +inherits = Extrudr XPETG CF; *PET06PG* + +[filament:Extrudr XPETG CF @PG 0.8] +inherits = Extrudr XPETG CF; *PET08PG* + [filament:Extrudr XPETG CF @MINI] inherits = Extrudr XPETG CF; *PETMINI* compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" @@ -2304,6 +3833,15 @@ filament_notes = "https://www.extrudr.com/en/products/catalogue/?material=199" first_layer_temperature = 230 temperature = 230 +[filament:Extrudr XPETG Matt @PG] +inherits = Extrudr XPETG Matt; *PETPG* + +[filament:Extrudr XPETG Matt @PG 0.6] +inherits = Extrudr XPETG Matt; *PET06PG* + +[filament:Extrudr XPETG Matt @PG 0.8] +inherits = Extrudr XPETG Matt; *PET08PG* + [filament:Extrudr XPETG Matt @MINI] inherits = Extrudr XPETG Matt; *PETMINI* @@ -2323,6 +3861,15 @@ slowdown_below_layer_time = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K43{endif} ; Filament gcode LA 1.0" filament_spool_weight = 230 +[filament:Extrudr BioFusion @PG] +inherits = Extrudr BioFusion; *PLAPG* + +[filament:Extrudr BioFusion @PG 0.6] +inherits = Extrudr BioFusion; *PLA06PG* + +[filament:Extrudr BioFusion @PG 0.8] +inherits = Extrudr BioFusion; *PLA08PG* + [filament:Extrudr Flax] inherits = *PLA* filament_vendor = Extrudr @@ -2338,6 +3885,20 @@ slowdown_below_layer_time = 20 filament_max_volumetric_speed = 11 filament_spool_weight = 262 +[filament:Extrudr Flax @PG] +inherits = Extrudr Flax; *PLAPG* +filament_max_volumetric_speed = 11 + +[filament:Extrudr Flax @PG 0.6] +inherits = Extrudr Flax @PG; *PLA06PG* +filament_max_volumetric_speed = 14 + +[filament:Extrudr Flax @PG 0.8] +inherits = Extrudr Flax @PG; *PLA08PG* +filament_max_volumetric_speed = 17 +first_layer_temperature = 200 +temperature = 200 + [filament:Extrudr GreenTEC] inherits = *PLA* filament_vendor = Extrudr @@ -2349,6 +3910,15 @@ temperature = 208 slowdown_below_layer_time = 20 filament_spool_weight = 262 +[filament:Extrudr GreenTEC @PG] +inherits = Extrudr GreenTEC; *PLAPG* + +[filament:Extrudr GreenTEC @PG 0.6] +inherits = Extrudr GreenTEC; *PLA06PG* + +[filament:Extrudr GreenTEC @PG 0.8] +inherits = Extrudr GreenTEC; *PLA08PG* + [filament:Extrudr GreenTEC Pro] inherits = *PLA* filament_vendor = Extrudr @@ -2362,8 +3932,17 @@ full_fan_speed_layer = 0 slowdown_below_layer_time = 20 filament_spool_weight = 230 +[filament:Extrudr GreenTEC Pro @PG] +inherits = Extrudr GreenTEC Pro; *PLAPG* + +[filament:Extrudr GreenTEC Pro @PG 0.6] +inherits = Extrudr GreenTEC Pro; *PLA06PG* + +[filament:Extrudr GreenTEC Pro @PG 0.8] +inherits = Extrudr GreenTEC Pro; *PLA08PG* + [filament:Extrudr GreenTEC Pro Carbon] -inherits = *PLA* +inherits = *PLA*; *04PLUS* filament_vendor = Extrudr filament_cost = 62.49 filament_density = 1.2 @@ -2375,7 +3954,15 @@ temperature = 225 full_fan_speed_layer = 0 slowdown_below_layer_time = 20 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Extrudr GreenTEC Pro Carbon @PG] +inherits = Extrudr GreenTEC Pro Carbon; *PLAPG* + +[filament:Extrudr GreenTEC Pro Carbon @PG 0.6] +inherits = Extrudr GreenTEC Pro Carbon; *PLA06PG* + +[filament:Extrudr GreenTEC Pro Carbon @PG 0.8] +inherits = Extrudr GreenTEC Pro Carbon; *PLA08PG* [filament:Extrudr PLA NX1] inherits = *PLA* @@ -2393,12 +3980,30 @@ min_fan_speed = 30 slowdown_below_layer_time = 20 filament_spool_weight = 262 +[filament:Extrudr PLA NX1 @PG] +inherits = Extrudr PLA NX1; *PLAPG* + +[filament:Extrudr PLA NX1 @PG 0.6] +inherits = Extrudr PLA NX1; *PLA06PG* + +[filament:Extrudr PLA NX1 @PG 0.8] +inherits = Extrudr PLA NX1; *PLA08PG* + [filament:Extrudr PLA NX2] inherits = Extrudr PLA NX1 filament_cost = 23.63 filament_density = 1.3 filament_notes = "https://www.extrudr.com/en/products/catalogue/?material=128" +[filament:Extrudr PLA NX2 @PG] +inherits = Extrudr PLA NX2; *PLAPG* + +[filament:Extrudr PLA NX2 @PG 0.6] +inherits = Extrudr PLA NX2; *PLA06PG* + +[filament:Extrudr PLA NX2 @PG 0.8] +inherits = Extrudr PLA NX2; *PLA08PG* + [filament:Extrudr Flex Hard] inherits = *FLEX* filament_vendor = Extrudr @@ -2414,6 +4019,17 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 +[filament:Extrudr Flex Hard @PG] +inherits = Extrudr Flex Hard; *FLEXPG* +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 + +[filament:Extrudr Flex Hard @PG 0.6] +inherits = Extrudr Flex Hard @PG; *FLEX06PG* + +[filament:Extrudr Flex Hard @PG 0.8] +inherits = Extrudr Flex Hard @PG; *FLEX08PG* + [filament:Extrudr Flex Medium] inherits = *FLEX* filament_vendor = Extrudr @@ -2429,6 +4045,17 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 +[filament:Extrudr Flex Medium @PG] +inherits = Extrudr Flex Medium; *FLEXPG* +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 + +[filament:Extrudr Flex Medium @PG 0.6] +inherits = Extrudr Flex Medium @PG; *FLEX06PG* + +[filament:Extrudr Flex Medium @PG 0.8] +inherits = Extrudr Flex Medium @PG; *FLEX08PG* + [filament:Extrudr Flex SemiSoft] inherits = *FLEX* filament_vendor = Extrudr @@ -2444,6 +4071,20 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 +[filament:Extrudr Flex SemiSoft @PG] +inherits = Extrudr Flex SemiSoft; *FLEXPG* +extrusion_multiplier = 1.1 +filament_retract_length = 3 +filament_max_volumetric_speed = 3 + +[filament:Extrudr Flex SemiSoft @PG 0.6] +inherits = Extrudr Flex SemiSoft @PG; *FLEX06PG* +filament_max_volumetric_speed = 5 + +[filament:Extrudr Flex SemiSoft @PG 0.8] +inherits = Extrudr Flex SemiSoft @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + [filament:addnorth Adamant S1] inherits = *FLEX* filament_vendor = addnorth @@ -2471,6 +4112,21 @@ filament_spool_weight = 0 filament_retract_restart_extra = 0.1 filament_wipe = nil +[filament:addnorth Adamant S1 @PG] +inherits = addnorth Adamant S1; *FLEXPG* +filament_max_volumetric_speed = 3 +filament_retract_length = 1.5 +filament_retract_restart_extra = 0 +filament_retract_lift = 0.2 + +[filament:addnorth Adamant S1 @PG 0.6] +inherits = addnorth Adamant S1 @PG; *FLEX06PG* +filament_max_volumetric_speed = 5.5 + +[filament:addnorth Adamant S1 @PG 0.8] +inherits = addnorth Adamant S1 @PG; *FLEX08PG* +filament_max_volumetric_speed = 9 + [filament:addnorth Adura X] inherits = *PET* filament_vendor = addnorth @@ -2496,7 +4152,21 @@ filament_retract_lift = 0.4 filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" filament_spool_weight = 0 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and printer_model!="MK2SMM" and ! single_extruder_multi_material + +[filament:addnorth Adura X @PG] +inherits = addnorth Adura X; *PETPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_max_volumetric_speed = 4 + +[filament:addnorth Adura X @PG 0.6] +inherits = addnorth Adura X @PG; *PET06PG* +filament_max_volumetric_speed = 6 + +[filament:addnorth Adura X @PG 0.8] +inherits = addnorth Adura X @PG; *PET08PG* +filament_max_volumetric_speed = 8 [filament:addnorth Adura X @MINI] inherits = addnorth Adura X @@ -2536,6 +4206,15 @@ full_fan_speed_layer = 3 slowdown_below_layer_time = 15 filament_spool_weight = 0 +[filament:addnorth E-PLA @PG] +inherits = addnorth E-PLA; *PLAPG* + +[filament:addnorth E-PLA @PG 0.6] +inherits = addnorth E-PLA; *PLA06PG* + +[filament:addnorth E-PLA @PG 0.8] +inherits = addnorth E-PLA; *PLA08PG* + [filament:addnorth ESD-PETG] inherits = *PET* filament_vendor = addnorth @@ -2560,6 +4239,18 @@ filament_retract_lift = 0 filament_max_volumetric_speed = 2 filament_spool_weight = 0 +[filament:addnorth ESD-PETG @PG] +inherits = addnorth ESD-PETG; *PETPG* +filament_max_volumetric_speed = 2 + +[filament:addnorth ESD-PETG @PG 0.6] +inherits = addnorth ESD-PETG @PG; *PET06PG* +filament_max_volumetric_speed = 3.5 + +[filament:addnorth ESD-PETG @PG 0.8] +inherits = addnorth ESD-PETG @PG; *PET08PG* +filament_max_volumetric_speed = 6 + [filament:addnorth ESD-PETG @MINI] inherits = addnorth ESD-PETG filament_retract_length = nil @@ -2601,6 +4292,19 @@ filament_deretract_speed = 25 filament_spool_weight = 0 filament_notes = "Use Magigoo PP bed adhesive or PP packing tape (on a cold printbed)." +[filament:addnorth OBC Polyethylene @PG] +inherits = addnorth OBC Polyethylene; *FLEXPG* +filament_max_volumetric_speed = 4 +filament_retract_length = 1.5 + +[filament:addnorth OBC Polyethylene @PG 0.6] +inherits = addnorth OBC Polyethylene @PG; *FLEX06PG* +filament_max_volumetric_speed = 6 + +[filament:addnorth OBC Polyethylene @PG 0.8] +inherits = addnorth OBC Polyethylene @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:addnorth PETG] inherits = *PET* filament_vendor = addnorth @@ -2623,6 +4327,15 @@ filament_retract_length = 1.4 filament_max_volumetric_speed = 8 filament_spool_weight = 0 +[filament:addnorth PETG @PG] +inherits = addnorth PETG; *PETPG* + +[filament:addnorth PETG @PG 0.6] +inherits = addnorth PETG @PG; *PET06PG* + +[filament:addnorth PETG @PG 0.8] +inherits = addnorth PETG @PG; *PET08PG* + [filament:addnorth PETG @MINI] inherits = addnorth PETG filament_retract_length = nil @@ -2662,7 +4375,19 @@ filament_retract_length = 1.4 filament_max_volumetric_speed = 5 filament_spool_weight = 0 filament_notes = "Please use a nozzle that is resistant to abrasive filaments, like hardened steel." -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and printer_model!="MK2SMM" and ! single_extruder_multi_material + +[filament:addnorth Rigid X @PG] +inherits = addnorth Rigid X; *PETPG*; *04PLUSPG* +filament_max_volumetric_speed = 5 + +[filament:addnorth Rigid X @PG 0.6] +inherits = addnorth Rigid X @PG; *PET06PG* +filament_max_volumetric_speed = 7 + +[filament:addnorth Rigid X @PG 0.8] +inherits = addnorth Rigid X @PG; *PET08PG* +filament_max_volumetric_speed = 10 [filament:addnorth Rigid X @MINI] inherits = addnorth Rigid X @@ -2696,7 +4421,16 @@ slowdown_below_layer_time = 15 min_print_speed = 20 filament_spool_weight = 0 filament_retract_length = 1 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:addnorth Textura @PG] +inherits = addnorth Textura; *PLAPG* + +[filament:addnorth Textura @PG 0.6] +inherits = addnorth Textura; *PLA06PG* + +[filament:addnorth Textura @PG 0.8] +inherits = addnorth Textura; *PLA08PG* [filament:addnorth Textura @MINI] inherits = addnorth Textura @@ -2724,7 +4458,19 @@ disable_fan_first_layers = 3 fan_below_layer_time = 60 slowdown_below_layer_time = 15 bridge_fan_speed = 20 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Filamentworld ABS @PG] +inherits = Filamentworld ABS; *ABSPG* +first_layer_bed_temperature = 100 + +[filament:Filamentworld ABS @PG 0.6] +inherits = Filamentworld ABS @PG; *ABS06PG* + +[filament:Filamentworld ABS @PG 0.8] +inherits = Filamentworld ABS @PG; *ABS08PG* +first_layer_temperature = 240 +temperature = 240 [filament:Filamentworld ABS @MINI] inherits = Filamentworld ABS @@ -2755,6 +4501,18 @@ filament_retract_length = 1.4 filament_max_volumetric_speed = 8 filament_spool_weight = 0 +[filament:Filamentworld PETG @PG] +inherits = Filamentworld PETG; *PETPG* +filament_max_volumetric_speed = 8 + +[filament:Filamentworld PETG @PG 0.6] +inherits = Filamentworld PETG @PG; *PET06PG* + +[filament:Filamentworld PETG @PG 0.8] +inherits = Filamentworld PETG @PG; *PET08PG* +first_layer_temperature = 240 +temperature = 245 + [filament:Filamentworld PETG @MINI] inherits = Filamentworld PETG filament_retract_length = nil @@ -2778,6 +4536,15 @@ slowdown_below_layer_time = 10 filament_spool_weight = 0 min_print_speed = 20 +[filament:Filamentworld PLA @PG] +inherits = Filamentworld PLA; *PLAPG* + +[filament:Filamentworld PLA @PG 0.6] +inherits = Filamentworld PLA; *PLA06PG* + +[filament:Filamentworld PLA @PG 0.8] +inherits = Filamentworld PLA; *PLA08PG* + [filament:Filament PM PETG] inherits = *PET* renamed_from = "Plasty Mladec PETG" @@ -2785,14 +4552,35 @@ filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.27 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Filament PM PETG @PG] +inherits = Filament PM PETG; *PETPG* + +[filament:Filament PM PETG @PG 0.6] +inherits = Filament PM PETG; *PET06PG* + +[filament:Filament PM PETG @PG 0.8] +inherits = Filament PM PETG; *PET08PG* [filament:Generic PLA] inherits = *PLA* filament_vendor = Generic filament_cost = 25.4 filament_density = 1.24 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Generic PLA @PG] +inherits = Generic PLA; *PLAPG* + +[filament:Generic PLA @PG 0.6] +inherits = Generic PLA; *PLA06PG* +filament_max_volumetric_speed = 15 + +[filament:Generic PLA @PG 0.8] +inherits = Generic PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 [filament:3D-Fuel Standard PLA] inherits = *PLA* @@ -2803,10 +4591,38 @@ filament_max_volumetric_speed = 10 first_layer_temperature = 210 temperature = 200 +[filament:3D-Fuel Standard PLA @PG] +inherits = 3D-Fuel Standard PLA; *PLAPG* +filament_max_volumetric_speed = 10 + +[filament:3D-Fuel Standard PLA @PG 0.6] +inherits = 3D-Fuel Standard PLA @PG; *PLA06PG* +filament_max_volumetric_speed = 13 + +[filament:3D-Fuel Standard PLA @PG 0.8] +inherits = 3D-Fuel Standard PLA @PG; *PLA08PG* +filament_max_volumetric_speed = 16 +first_layer_temperature = 210 +temperature = 210 + [filament:3D-Fuel EasiPrint PLA] inherits = 3D-Fuel Standard PLA filament_cost = 30.44 +[filament:3D-Fuel EasiPrint PLA @PG] +inherits = 3D-Fuel EasiPrint PLA; *PLAPG* +filament_max_volumetric_speed = 10 + +[filament:3D-Fuel EasiPrint PLA @PG 0.6] +inherits = 3D-Fuel EasiPrint PLA; *PLA06PG* +filament_max_volumetric_speed = 13 + +[filament:3D-Fuel EasiPrint PLA @PG 0.8] +inherits = 3D-Fuel EasiPrint PLA; *PLA08PG* +filament_max_volumetric_speed = 16 +first_layer_temperature = 210 +temperature = 210 + [filament:3D-Fuel Pro PLA] inherits = *PLA* filament_vendor = 3D-Fuel @@ -2817,6 +4633,20 @@ first_layer_temperature = 220 temperature = 215 filament_retract_lift = 0 +[filament:3D-Fuel Pro PLA @PG] +inherits = 3D-Fuel Pro PLA; *PLAPG* +filament_max_volumetric_speed = 12 + +[filament:3D-Fuel Pro PLA @PG 0.6] +inherits = 3D-Fuel Pro PLA @PG; *PLA06PG* +filament_max_volumetric_speed = 14 + +[filament:3D-Fuel Pro PLA @PG 0.8] +inherits = 3D-Fuel Pro PLA @PG; *PLA08PG* +filament_max_volumetric_speed = 17 +first_layer_temperature = 225 +temperature = 225 + [filament:3D-Fuel Buzzed] inherits = 3D-Fuel Standard PLA filament_cost = 44.27 @@ -2825,6 +4655,20 @@ first_layer_temperature = 210 temperature = 195 filament_max_volumetric_speed = 8 +[filament:3D-Fuel Buzzed @PG] +inherits = 3D-Fuel Buzzed; *PLAPG* +filament_max_volumetric_speed = 8 + +[filament:3D-Fuel Buzzed @PG 0.6] +inherits = 3D-Fuel Buzzed @PG; *PLA06PG* +filament_max_volumetric_speed = 10 + +[filament:3D-Fuel Buzzed @PG 0.8] +inherits = 3D-Fuel Buzzed @PG; *PLA08PG* +filament_max_volumetric_speed = 12 +first_layer_temperature = 210 +temperature = 210 + [filament:3D-Fuel Wound up] inherits = 3D-Fuel Buzzed filament_cost = 44.27 @@ -2833,12 +4677,36 @@ first_layer_temperature = 215 temperature = 210 filament_max_volumetric_speed = 8 +[filament:3D-Fuel Wound up @PG] +inherits = 3D-Fuel Wound up; *PLAPG* +filament_max_volumetric_speed = 8 + +[filament:3D-Fuel Wound up @PG 0.6] +inherits = 3D-Fuel Wound up @PG; *PLA06PG* +filament_max_volumetric_speed = 10 + +[filament:3D-Fuel Wound up @PG 0.8] +inherits = 3D-Fuel Wound up @PG; *PLA08PG* +filament_max_volumetric_speed = 12 +first_layer_temperature = 220 +temperature = 220 + [filament:3D-Fuel Workday ABS] inherits = *ABSC* filament_vendor = 3D-Fuel filament_cost = 23.25 filament_density = 1.04 +[filament:3D-Fuel Workday ABS @PG] +inherits = 3D-Fuel Workday ABS; *ABSPG* +bed_temperature = 105 + +[filament:3D-Fuel Workday ABS @PG 0.6] +inherits = 3D-Fuel Workday ABS @PG; *ABS06PG* + +[filament:3D-Fuel Workday ABS @PG 0.8] +inherits = 3D-Fuel Workday ABS @PG; *ABS08PG* + [filament:3D-Fuel Workday ABS @MINI] inherits = 3D-Fuel Workday ABS; *ABSMINI* @@ -2849,6 +4717,18 @@ filament_cost = 21 filament_density = 1.24 filament_max_volumetric_speed = 12 +[filament:Jessie PLA @PG] +inherits = Jessie PLA; *PLAPG* +filament_max_volumetric_speed = 12 + +[filament:Jessie PLA @PG 0.6] +inherits = Jessie PLA @PG; *PLA06PG* +filament_max_volumetric_speed = 14 + +[filament:Jessie PLA @PG 0.8] +inherits = Jessie PLA @PG; *PLA08PG* +filament_max_volumetric_speed = 17 + [filament:Jessie PETG] inherits = *PET* filament_vendor = Printed Solid @@ -2860,6 +4740,20 @@ temperature = 245 bed_temperature = 90 filament_max_volumetric_speed = 7 +[filament:Jessie PETG @PG] +inherits = Jessie PETG; *PETPG* +filament_max_volumetric_speed = 7 + +[filament:Jessie PETG @PG 0.6] +inherits = Jessie PETG @PG; *PET06PG* +filament_max_volumetric_speed = 16 + +[filament:Jessie PETG @PG 0.8] +inherits = Jessie PETG @PG; *PET08PG* +filament_max_volumetric_speed = 20 +first_layer_temperature = 245 +temperature = 255 + [filament:Jessie PETG @MINI] inherits = Jessie PETG; *PETMINI* @@ -2870,6 +4764,15 @@ filament_cost = 20.99 filament_density = 1.24 filament_spool_weight = 250 +[filament:Devil Design PLA @PG] +inherits = Devil Design PLA; *PLAPG* + +[filament:Devil Design PLA @PG 0.6] +inherits = Devil Design PLA; *PLA06PG* + +[filament:Devil Design PLA @PG 0.8] +inherits = Devil Design PLA; *PLA08PG* + [filament:Devil Design PETG] inherits = *PET* filament_vendor = Devil Design @@ -2881,12 +4784,30 @@ first_layer_bed_temperature = 85 temperature = 230 bed_temperature = 90 +[filament:Devil Design PETG @PG] +inherits = Devil Design PETG; *PETPG* + +[filament:Devil Design PETG @PG 0.6] +inherits = Devil Design PETG; *PET06PG* + +[filament:Devil Design PETG @PG 0.8] +inherits = Devil Design PETG; *PET08PG* + [filament:Spectrum PLA] inherits = *PLA* filament_vendor = Spectrum filament_cost = 21.50 filament_density = 1.24 +[filament:Spectrum PLA @PG] +inherits = Spectrum PLA; *PLAPG* + +[filament:Spectrum PLA @PG 0.6] +inherits = Spectrum PLA; *PLA06PG* + +[filament:Spectrum PLA @PG 0.8] +inherits = Spectrum PLA; *PLA08PG* + [filament:Generic FLEX] inherits = *FLEX* filament_vendor = Generic @@ -2896,7 +4817,29 @@ filament_max_volumetric_speed = 1.2 filament_retract_length = 0 filament_retract_speed = nil filament_retract_lift = nil -compatible_printers_condition = nozzle_diameter[0]>0.35 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.35 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Generic FLEX @PG] +inherits = Generic FLEX; *FLEXPG* +filament_max_volumetric_speed = 3 +filament_retract_length = 2.5 +fan_always_on = 1 +min_fan_speed = 30 +max_fan_speed = 30 +cooling = 1 +filament_retract_lift = 0 +slowdown_below_layer_time = 10 +first_layer_temperature = 230 +temperature = 230 +extrusion_multiplier = 1.08 + +[filament:Generic FLEX @PG 0.6] +inherits = Generic FLEX @PG; *FLEX06PG* +filament_max_volumetric_speed = 6 + +[filament:Generic FLEX @PG 0.8] +inherits = Generic FLEX @PG; *FLEX08PG* +filament_max_volumetric_speed = 9 [filament:Fillamentum Flexfill 92A] inherits = *FLEX* @@ -2915,6 +4858,20 @@ min_fan_speed = 60 disable_fan_first_layers = 4 full_fan_speed_layer = 6 +[filament:Fillamentum Flexfill 92A @PG] +inherits = Fillamentum Flexfill 92A; *FLEXPG* +filament_max_volumetric_speed = 3.5 +extrusion_multiplier = 1.1 +filament_retract_length = 3.5 + +[filament:Fillamentum Flexfill 92A @PG 0.6] +inherits = Fillamentum Flexfill 92A @PG; *FLEX06PG* +filament_max_volumetric_speed = 6.5 + +[filament:Fillamentum Flexfill 92A @PG 0.8] +inherits = Fillamentum Flexfill 92A @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:AmazonBasics TPU] inherits = *FLEX* filament_vendor = AmazonBasics @@ -2942,6 +4899,19 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 +[filament:AmazonBasics TPU @PG] +inherits = AmazonBasics TPU; *FLEXPG* +filament_retract_length = 2.5 +extrusion_multiplier = 1.1 + +[filament:AmazonBasics TPU @PG 0.6] +inherits = AmazonBasics TPU @PG; *FLEX06PG* +filament_max_volumetric_speed = 6.5 + +[filament:AmazonBasics TPU @PG 0.8] +inherits = AmazonBasics TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:SainSmart TPU] inherits = *FLEX* filament_vendor = SainSmart @@ -2969,6 +4939,21 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 +[filament:SainSmart TPU @PG] +inherits = SainSmart TPU; *FLEXPG* +filament_max_volumetric_speed = 5 +first_layer_temperature = 235 +temperature = 235 +filament_retract_length = 1.5 + +[filament:SainSmart TPU @PG 0.6] +inherits = SainSmart TPU @PG; *FLEX06PG* +filament_max_volumetric_speed = 7 + +[filament:SainSmart TPU @PG 0.8] +inherits = SainSmart TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:NinjaTek NinjaFlex TPU] inherits = *FLEX* filament_vendor = NinjaTek @@ -2996,6 +4981,20 @@ min_print_speed = 10 slowdown_below_layer_time = 10 cooling = 1 +[filament:NinjaTek NinjaFlex TPU @PG] +inherits = NinjaTek NinjaFlex TPU; *FLEXPG* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3.5 +extrusion_multiplier = 1.12 + +[filament:NinjaTek NinjaFlex TPU @PG 0.6] +inherits = NinjaTek NinjaFlex TPU @PG; *FLEX06PG* +filament_max_volumetric_speed = 6.5 + +[filament:NinjaTek NinjaFlex TPU @PG 0.8] +inherits = NinjaTek NinjaFlex TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:NinjaTek Cheetah TPU] inherits = NinjaTek NinjaFlex TPU filament_retract_length = 1.5 @@ -3007,6 +5006,19 @@ filament_deretract_speed = 25 first_layer_temperature = 240 temperature = 240 +[filament:NinjaTek Cheetah TPU @PG] +inherits = NinjaTek Cheetah TPU; *FLEXPG* +filament_max_volumetric_speed = 6 +filament_retract_length = 2.2 + +[filament:NinjaTek Cheetah TPU @PG 0.6] +inherits = NinjaTek Cheetah TPU @PG; *FLEX06PG* +filament_max_volumetric_speed = 8 + +[filament:NinjaTek Cheetah TPU @PG 0.8] +inherits = NinjaTek Cheetah TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 12 + [filament:NinjaTek Cheetah TPU @MINI] inherits = NinjaTek NinjaFlex TPU; *FLEXMINI* filament_density = 1.22 @@ -3045,6 +5057,19 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 +[filament:Filatech FilaFlex40 @PG] +inherits = Filatech FilaFlex40; *FLEXPG* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.5 + +[filament:Filatech FilaFlex40 @PG 0.6] +inherits = Filatech FilaFlex40 @PG; *FLEX06PG* +filament_max_volumetric_speed = 5 + +[filament:Filatech FilaFlex40 @PG 0.8] +inherits = Filatech FilaFlex40 @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:Filatech FilaFlex30] inherits = Filatech FilaFlex40 temperature = 225 @@ -3052,6 +5077,19 @@ filament_density = 1.15 extrusion_multiplier = 1.1 filament_cost = +[filament:Filatech FilaFlex30 @PG] +inherits = Filatech FilaFlex30; *FLEXPG* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3 + +[filament:Filatech FilaFlex30 @PG 0.6] +inherits = Filatech FilaFlex30 @PG; *FLEX06PG* +filament_max_volumetric_speed = 7 + +[filament:Filatech FilaFlex30 @PG 0.8] +inherits = Filatech FilaFlex30 @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:Filatech FilaFlex55] inherits = Filatech FilaFlex40 temperature = 230 @@ -3063,6 +5101,15 @@ filament_cost = first_layer_temperature = 235 extrusion_multiplier = 1 +# [filament:Filatech FilaFlex55 @PG] +# inherits = Filatech FilaFlex55; *FLEXPG* + +# [filament:Filatech FilaFlex55 @PG 0.6] +# inherits = Filatech FilaFlex55 @PG; *FLEX06PG* + +# [filament:Filatech FilaFlex55 @PG 0.8] +# inherits = Filatech FilaFlex55 @PG; *FLEX08PG* + # [filament:Filatech TPE] # inherits = Filatech FilaFlex40 # first_layer_temperature = 230 @@ -3083,6 +5130,20 @@ min_fan_speed = 80 fan_always_on = 1 temperature = 235 +[filament:Filatech TPU @PG] +inherits = Filatech TPU; *FLEXPG* +filament_max_volumetric_speed = 5.5 +first_layer_temperature = 235 +filament_retract_length = 2.2 + +[filament:Filatech TPU @PG 0.6] +inherits = Filatech TPU @PG; *FLEX06PG* +filament_max_volumetric_speed = 7 + +[filament:Filatech TPU @PG 0.8] +inherits = Filatech TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:Filatech ABS] inherits = *ABSC* filament_vendor = Filatech @@ -3090,6 +5151,16 @@ filament_cost = extrusion_multiplier = 0.95 filament_density = 1.05 +[filament:Filatech ABS @PG] +inherits = Filatech ABS; *ABSPG* +bed_temperature = 105 + +[filament:Filatech ABS @PG 0.6] +inherits = Filatech ABS @PG; *ABS06PG* + +[filament:Filatech ABS @PG 0.8] +inherits = Filatech ABS @PG; *ABS08PG* + [filament:Filatech ABS @MINI] inherits = Filatech ABS; *ABSMINI* @@ -3101,7 +5172,17 @@ extrusion_multiplier = 0.95 filament_density = 1.1 first_layer_bed_temperature = 105 bed_temperature = 100 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Filatech FilaCarbon @PG] +inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG* +first_layer_bed_temperature = 100 + +[filament:Filatech FilaCarbonc 0.6] +inherits = Filatech FilaCarbon @PG; *ABS06PG* + +[filament:Filatech FilaCarbon @PG 0.8] +inherits = Filatech FilaCarbon @PG; *ABS08PG* [filament:Filatech FilaCarbon @MINI] inherits = Filatech FilaCarbon; *ABSMINI* @@ -3118,6 +5199,15 @@ first_layer_bed_temperature = 50 temperature = 230 bed_temperature = 55 +[filament:Filatech FilaPLA @PG] +inherits = Filatech FilaPLA; *PLAPG* + +[filament:Filatech FilaPLA @PG 0.6] +inherits = Filatech FilaPLA; *PLA06PG* + +[filament:Filatech FilaPLA @PG 0.8] +inherits = Filatech FilaPLA; *PLA08PG* + [filament:Filatech PLA] inherits = *PLA* filament_vendor = Filatech @@ -3126,10 +5216,28 @@ filament_density = 1.25 first_layer_temperature = 215 temperature = 210 +[filament:Filatech PLA @PG] +inherits = Filatech PLA; *PLAPG* + +[filament:Filatech PLA @PG 0.6] +inherits = Filatech PLA; *PLA06PG* + +[filament:Filatech PLA @PG 0.8] +inherits = Filatech PLA; *PLA08PG* + [filament:Filatech PLA+] inherits = Filatech PLA filament_density = 1.24 +[filament:Filatech PLA+ @PG] +inherits = Filatech PLA+; *PLAPG* + +[filament:Filatech PLA+ @PG 0.6] +inherits = Filatech PLA+; *PLA06PG* + +[filament:Filatech PLA+ @PG 0.8] +inherits = Filatech PLA+; *PLA08PG* + [filament:Filatech FilaTough] inherits = Filatech ABS filament_cost = @@ -3140,7 +5248,15 @@ first_layer_bed_temperature = 80 temperature = 240 bed_temperature = 90 cooling = 0 -compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech FilaTough @PG] +inherits = Filatech FilaTough; *ABSPG* + +[filament:Filatech FilaTough @PG 0.6] +inherits = Filatech FilaTough; *ABS06PG* + +[filament:Filatech FilaTough @PG 0.8] +inherits = Filatech FilaTough; *ABS08PG* [filament:Filatech HIPS] inherits = Prusa HIPS @@ -3152,7 +5268,27 @@ first_layer_temperature = 230 first_layer_bed_temperature = 100 temperature = 225 bed_temperature = 110 -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Filatech HIPS @PG] +inherits = Filatech HIPS; *ABSPG* +bridge_fan_speed = 50 +cooling = 1 +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 10 +first_layer_temperature = 220 +temperature = 225 +max_fan_speed = 20 +min_fan_speed = 20 +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:Filatech HIPS @PG 0.6] +inherits = Filatech HIPS @PG; *ABS06PG* + +[filament:Filatech HIPS @PG 0.8] +inherits = Filatech HIPS @PG; *ABS08PG* [filament:Filatech HIPS @MINI] inherits = Filatech HIPS; *ABSMINI* @@ -3171,7 +5307,21 @@ cooling = 0 bridge_fan_speed = 25 filament_type = PA filament_max_volumetric_speed = 8 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Filatech PA @PG] +inherits = Filatech PA; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_max_volumetric_speed = 8 + +[filament:Filatech PA @PG 0.6] +inherits = Filatech PA @PG; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Filatech PA @PG 0.8] +inherits = Filatech PA @PG; *ABS08PG* +filament_max_volumetric_speed = 12 [filament:Filatech PA @MK2] inherits = Filatech PA @@ -3192,6 +5342,15 @@ bed_temperature = 115 filament_density = 1.2 filament_type = PC +[filament:Filatech PC @PG] +inherits = Filatech PC; *PCPG* + +[filament:Filatech PC @PG 0.6] +inherits = Filatech PC @PG; *PC06PG* + +[filament:Filatech PC @PG 0.8] +inherits = Filatech PC @PG; *PC08PG* + [filament:Filatech PC @MK2] inherits = Filatech PC first_layer_bed_temperature = 105 @@ -3211,6 +5370,15 @@ cooling = 1 extrusion_multiplier = 0.95 disable_fan_first_layers = 6 +[filament:Filatech PC-ABS @PG] +inherits = Filatech PC-ABS; *PCPG* + +[filament:Filatech PC-ABS @PG 0.6] +inherits = Filatech PC-ABS; *PC06PG* + +[filament:Filatech PC-ABS @PG 0.8] +inherits = Filatech PC-ABS; *PC08PG* + [filament:Filatech PC-ABS @MK2] inherits = Filatech PC-ABS first_layer_bed_temperature = 105 @@ -3229,15 +5397,32 @@ bed_temperature = 80 extrusion_multiplier = 0.95 fan_always_on = 0 +[filament:Filatech PETG @PG] +inherits = Filatech PETG; *PETPG* + +[filament:Filatech PETG @PG 0.6] +inherits = Filatech PETG; *PET06PG* + +[filament:Filatech PETG @PG 0.8] +inherits = Filatech PETG; *PET08PG* + [filament:Filatech PETG @MINI] inherits = Filatech PETG; *PETMINI* [filament:Filatech Wood-PLA] -inherits = Filatech PLA +inherits = Filatech PLA; *04PLUS* filament_cost = filament_density = 1.05 first_layer_temperature = 210 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech Wood-PLA @PG] +inherits = Filatech Wood-PLA; *PLAPG* + +[filament:Filatech Wood-PLA @PG 0.6] +inherits = Filatech Wood-PLA; *PLA06PG* + +[filament:Filatech Wood-PLA @PG 0.8] +inherits = Filatech Wood-PLA; *PLA08PG* [filament:Ultrafuse PET] inherits = *PET* @@ -3264,6 +5449,15 @@ filament_wipe = 0 filament_retract_layer_change = 0 filament_retract_lift = 0 +[filament:Ultrafuse PET @PG] +inherits = Ultrafuse PET; *PETPG* + +[filament:Ultrafuse PET @PG 0.6] +inherits = Ultrafuse PET; *PET06PG* + +[filament:Ultrafuse PET @PG 0.8] +inherits = Ultrafuse PET; *PET08PG* + [filament:Ultrafuse PET @MINI] inherits = Ultrafuse PET; *PETMINI* @@ -3280,7 +5474,16 @@ filament_retract_lift = 0 filament_retract_speed = 40 filament_retract_before_travel = 2 filament_retract_layer_change = 0 -compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Ultrafuse PRO1 @PG] +inherits = Ultrafuse PRO1; *PLAPG* + +[filament:Ultrafuse PRO1 @PG 0.6] +inherits = Ultrafuse PRO1; *PLA06PG* + +[filament:Ultrafuse PRO1 @PG 0.8] +inherits = Ultrafuse PRO1; *PLA08PG* [filament:Ultrafuse PRO1 @MINI] inherits = Ultrafuse PRO1 @@ -3305,6 +5508,17 @@ filament_retract_before_travel = 2 filament_wipe = 0 filament_retract_layer_change = 0 +[filament:Ultrafuse ABS @PG] +inherits = Ultrafuse ABS; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 100 + +[filament:Ultrafuse ABS @PG 0.6] +inherits = Ultrafuse ABS @PG; *ABS06PG* + +[filament:Ultrafuse ABS @PG 0.8] +inherits = Ultrafuse ABS @PG; *ABS08PG* + [filament:Ultrafuse ABS @MINI] inherits = Ultrafuse ABS; *ABSMINI* filament_retract_layer_change = nil @@ -3322,6 +5536,17 @@ filament_wipe = nil filament_retract_layer_change = 0 filament_retract_lift = 0 +[filament:Ultrafuse ABS Fusion+ @PG] +inherits = Ultrafuse ABS Fusion+; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 100 + +[filament:Ultrafuse ABS Fusion+ @PG 0.6] +inherits = Ultrafuse ABS Fusion+ @PG; *ABS06PG* + +[filament:Ultrafuse ABS Fusion+ @PG 0.8] +inherits = Ultrafuse ABS Fusion+ @PG; *ABS08PG* + [filament:Ultrafuse ABS Fusion+ @MINI] inherits = Ultrafuse ABS Fusion+; *ABSMINI* first_layer_bed_temperature = 100 @@ -3343,6 +5568,20 @@ disable_fan_first_layers = 4 filament_max_volumetric_speed = 5 filament_notes = "Material Description\nUltrafuse ASA is a high-performance thermoplastic with similar mechanical properties as ABS. ASA offers additional benefits such as high outdoor weather resistance. The UV resistance, toughness, and rigidity make it an ideal material to 3D-print outdoor fixtures and appliances without losing its properties or color. When also taking into account the high heat resistance and high chemical resistance, this filament is a good choice for many types of applications.\n\nPrinting Recommendations:\nApply Magigoo PC, 3D lac or Dimafix to a clean build plate to improve adhesion." +[filament:Ultrafuse ASA @PG] +inherits = Ultrafuse ASA; *ABSPG* +first_layer_bed_temperature = 105 +bed_temperature = 105 +filament_max_volumetric_speed = 5 + +[filament:Ultrafuse ASA @PG 0.6] +inherits = Ultrafuse ASA @PG; *ABS06PG* +filament_max_volumetric_speed = 9 + +[filament:Ultrafuse ASA @PG 0.8] +inherits = Ultrafuse ASA @PG; *ABS08PG* +filament_max_volumetric_speed = 12 + [filament:Ultrafuse ASA @MINI] inherits = Ultrafuse ASA; *ABSMINI* filament_type = ASA @@ -3357,6 +5596,17 @@ max_fan_speed = 20 filament_soluble = 1 filament_notes = "Material Description\nUltrafuse HIPS is a high-quality engineering thermoplastic, which is well known in the 3D-printing industry as a support material for ABS. But this material has additional properties to offer like good impact resistance, good dimensional stability, and easy post-processing. HiPS is a great material to use as a support for ABS because there is a good compatibility between the two materials, and HIPS is an easy breakaway support. Now you have the opportunity to create ABS models with complex geometry. HIPS is easy to post process with glue or with sanding paper." +[filament:Ultrafuse HIPS @PG] +inherits = Ultrafuse HIPS; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 100 + +[filament:Ultrafuse HIPS @PG 0.6] +inherits = Ultrafuse HIPS @PG; *ABS06PG* + +[filament:Ultrafuse HIPS @PG 0.8] +inherits = Ultrafuse HIPS @PG; *ABS08PG* + [filament:Ultrafuse HIPS @MINI] inherits = Ultrafuse HIPS; *ABSMINI* filament_type = HIPS @@ -3385,10 +5635,22 @@ filament_retract_before_travel = 2 filament_retract_layer_change = 0 filament_cost = 0 filament_spool_weight = 0 -compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" filament_notes = "Material Description\nThe key features of Ultrafuse PA are the high strength and high modulus. Furthermore, Ultrafuse PA shows a good thermal distortion stability.\n\nPrinting Recommendations:\nApply PVA glue, Kapton tape or PA adhesive to a clean buildplate to improve adhesion." +[filament:Ultrafuse PA @PG] +inherits = Ultrafuse PA; *ABSPG* +filament_max_volumetric_speed = 8 + +[filament:Ultrafuse PA @PG 0.6] +inherits = Ultrafuse PA @PG; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Ultrafuse PA @PG 0.8] +inherits = Ultrafuse PA @PG; *ABS08PG* +filament_max_volumetric_speed = 12 + [filament:Ultrafuse PA6 GF30] inherits = Ultrafuse PA filament_density = 1.17 @@ -3410,13 +5672,29 @@ filament_retract_speed = 40 filament_deretract_speed = 30 filament_retract_lift = nil filament_wipe = 0 -filament_notes = "Material Description\nUltrafuse® PA6 GF30 is a unique compound specifically developed for FFF printing. Due to the glass fiber content of 30%, parts tend to warp less. In addition the excellent layer adhesion and its compatibility with the water soluble support Ultrafuse® BVOH make this material the perfect solution to develop industrial applications on an FFF printer.\n\nWith its high wear and chemical resistance, high stiffness and strength, Ultrafuse® PA6 GF30 is perfect for a wide variety of applications in automotive, electronics or transportation.\n\nUltrafuse PA6 GF30 is designed for functional prototyping and demanding applications such as industrial tooling, transportation, electronics, small appliances, sports & leisure\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened or Ruby nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PA6 GF30 can be printed directly onto a clean build plate. For challenging prints, use Magigoo PA gluestick to improve adhesion." -compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +filament_notes = "Material Description\nUltrafuse® PA6 GF30 is a unique compound specifically developed for FFF printing. Due to the glass fiber content of 30%, parts tend to warp less. In addition the excellent layer adhesion and its compatibility with the water soluble support Ultrafuse® BVOH make this material the perfect solution to develop industrial applications on an FFF printer.\n\nWith its high wear and chemical resistance, high stiffness and strength, Ultrafuse® PA6 GF30 is perfect for a wide variety of applications in automotive, electronics or transportation.\n\nUltrafuse PA6 GF30 is designed for functional prototyping and demanding applications such as industrial tooling, transportation, electronics, small appliances, sports & leisure\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PA6 GF30 can be printed directly onto a clean build plate. For challenging prints, use Magigoo PA gluestick to improve adhesion." +compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Ultrafuse PA6 GF30 @PG 0.6] +inherits = Ultrafuse PA6 GF30; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Ultrafuse PA6 GF30 @PG 0.8] +inherits = Ultrafuse PA6 GF30; *ABS08PG* +filament_max_volumetric_speed = 14 +first_layer_temperature = 275 +temperature = 275 [filament:Ultrafuse PAHT-CF15] inherits = Ultrafuse PA6 GF30 filament_density = 1.23 -filament_notes = "Material Description\nPAHT CF15 is a high-performance 3D printing filament that opens new application fields in FFF printing. In parallel to its advanced mechanical properties, dimensional stability, and chemical resistance, it has very good processability. It works in any FFF printer with a hardened nozzle. In addition to that, it is compatible with water-soluble support material and HiPS, which allow printing complex geometries that work in challenging environments. PAHT CF15 has high heat resistance up to 130 °C and low moisture absorption.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened or Ruby nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PAHT-CF can be printed directly onto a clean build plate. For challenging prints, use 3dLac to improve adhesion." +filament_notes = "Material Description\nPAHT CF15 is a high-performance 3D printing filament that opens new application fields in FFF printing. In parallel to its advanced mechanical properties, dimensional stability, and chemical resistance, it has very good processability. It works in any FFF printer with a hardened nozzle. In addition to that, it is compatible with water-soluble support material and HiPS, which allow printing complex geometries that work in challenging environments. PAHT CF15 has high heat resistance up to 130 °C and low moisture absorption.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PAHT-CF can be printed directly onto a clean build plate. For challenging prints, use 3dLac to improve adhesion." + +[filament:Ultrafuse PAHT-CF15 @PG 0.6] +inherits = Ultrafuse PAHT-CF15; *ABS06PG* + +[filament:Ultrafuse PAHT-CF15 @PG 0.8] +inherits = Ultrafuse PAHT-CF15; *ABS08PG* [filament:Ultrafuse PC-ABS-FR] inherits = Ultrafuse ABS @@ -3433,10 +5711,24 @@ min_fan_speed = 20 max_fan_speed = 20 bridge_fan_speed = 30 disable_fan_first_layers = 4 -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS – two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion." start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Ultrafuse PC-ABS-FR @PG] +inherits = Ultrafuse PC-ABS-FR; *ABSPG* +first_layer_bed_temperature = 105 +bed_temperature = 105 +filament_max_volumetric_speed = 8 + +[filament:Ultrafuse PC-ABS-FR @PG 0.6] +inherits = Ultrafuse PC-ABS-FR @PG; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Ultrafuse PC-ABS-FR @PG 0.8] +inherits = Ultrafuse PC-ABS-FR @PG; *ABS08PG* +filament_max_volumetric_speed = 12 + [filament:Ultrafuse PET-CF15] inherits = Ultrafuse PET filament_density = 1.36 @@ -3459,8 +5751,18 @@ filament_deretract_speed = 30 filament_retract_lift = nil filament_wipe = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" -filament_notes = "Material Description\nPET CF15 is a Carbon Fiber reinforced PET which has precisely tuned material properties, for a wide range of technical applications. The filament is very strong and stiff and has high heat resistance. With its high dimensional stability and low abrasiveness, the filament offers an easy to print experience which allows direct printing on glass or a PEI sheet. It is compatible with HiPS for breakaway support and water soluble support and has an excellent surface finish.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened or Ruby nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PET-CF15 can be printed directly onto a clean build plate. For challenging prints, use 3dLac to improve adhesion." -compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +filament_notes = "Material Description\nPET CF15 is a Carbon Fiber reinforced PET which has precisely tuned material properties, for a wide range of technical applications. The filament is very strong and stiff and has high heat resistance. With its high dimensional stability and low abrasiveness, the filament offers an easy to print experience which allows direct printing on glass or a PEI sheet. It is compatible with HiPS for breakaway support and water soluble support and has an excellent surface finish.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PET-CF15 can be printed directly onto a clean build plate. For challenging prints, use 3dLac to improve adhesion." +compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Ultrafuse PET-CF15 @PG 0.6] +inherits = Ultrafuse PET; *PET06PG* +filament_max_volumetric_speed = 10 + +[filament:Ultrafuse PET-CF15 @PG 0.8] +inherits = Ultrafuse PET; *PET08PG* +filament_max_volumetric_speed = 13 +first_layer_temperature = 270 +temperature = 275 [filament:Ultrafuse PLA] inherits = *PLA* @@ -3469,6 +5771,15 @@ filament_density = 1.25 full_fan_speed_layer = 3 filament_notes = "Material Description\nPLA is one of the most used materials for 3D printing. Ultrafuse PLA is available in a wide range of colors. The glossy feel often attracts those who print display models or items for household use. Many appreciate the plant-based origin of this material. When properly cooled, PLA has a high maximum printing speed and sharp printed corners. Combining this with low warping of the print makes it a popular plastic for home printers, hobbyists, prototyping and schools.\n\nPrinting Recommendations:\nUltrafuse PLA can be printed directly onto a clean build plate." +[filament:Ultrafuse PLA @PG] +inherits = Ultrafuse PLA; *PLAPG* + +[filament:Ultrafuse PLA @PG 0.6] +inherits = Ultrafuse PLA; *PLA06PG* + +[filament:Ultrafuse PLA @PG 0.8] +inherits = Ultrafuse PLA; *PLA08PG* + [filament:Ultrafuse PP] inherits = Ultrafuse ABS filament_density = 0.91 @@ -3492,7 +5803,19 @@ filament_deretract_speed = 25 filament_retract_layer_change = 0 filament_wipe = nil filament_notes = "Material Description\nUltrafuse PP is high-performance thermoplastic with low density, high elasticity and high resistance to fatigue. The mechanical properties make it an ideal material for 3D-printing applications which have to endure high stress or strain. The filament has high chemical resistance and a high isolation value. PP is one of the most used materials in the world, due to its versatility and ability to engineer lightweight tough parts.\n\nPrinting Recommendations:\nApply PP tape or Magigoo PP adhesive to the buildplate for optimal adhesion." -compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Ultrafuse PP @PG] +inherits = Ultrafuse PP; *ABSPG* +filament_max_volumetric_speed = 2.5 + +[filament:Ultrafuse PP @PG 0.6] +inherits = Ultrafuse PP @PG; *ABS06PG* +filament_max_volumetric_speed = 4 + +[filament:Ultrafuse PP @PG 0.8] +inherits = Ultrafuse PP @PG; *ABS08PG* +filament_max_volumetric_speed = 6 [filament:Ultrafuse PP-GF30] inherits = Ultrafuse PP @@ -3513,8 +5836,14 @@ filament_retract_speed = 40 filament_deretract_speed = 30 filament_retract_lift = nil filament_wipe = 0 -filament_notes = "Ultrafuse PP GF30 is polypropylene, reinforced with 30% glass fiber content. The fibers in this compound are specially designed for 3D-printing filaments and are compatible with a wide range of standard FFF 3D-printers. The extreme stiffness makes this material highly suitable for demanding applications. Other key properties of PPGF30 are high heat resistance and improved UV-resistance. All these excellent properties make this filament highly suitable in an industrial environment.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened or Ruby nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nApply PP strapping tape or PPGF adhesive to a clean build plate for optimal adhesion." -compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +filament_notes = "Ultrafuse PP GF30 is polypropylene, reinforced with 30% glass fiber content. The fibers in this compound are specially designed for 3D-printing filaments and are compatible with a wide range of standard FFF 3D-printers. The extreme stiffness makes this material highly suitable for demanding applications. Other key properties of PPGF30 are high heat resistance and improved UV-resistance. All these excellent properties make this filament highly suitable in an industrial environment.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nApply PP strapping tape or PPGF adhesive to a clean build plate for optimal adhesion." +compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Ultrafuse PP-GF30 @PG 0.6] +inherits = Ultrafuse PP; *ABS06PG* + +[filament:Ultrafuse PP-GF30 @PG 0.8] +inherits = Ultrafuse PP; *ABS08PG* [filament:Ultrafuse TPC-45D] inherits = *FLEX* @@ -3545,6 +5874,16 @@ filament_retract_before_travel = 2 filament_retract_layer_change = 0 filament_notes = "Material Description\nTPC 45D is a flexible, shore 45D, rubber-like Thermoplastic Copolyester Elastomer (TPE-C), which is derived from rapeseed oil and combines the best properties of elastomers (rubbers) and polyesters. The material delivers excellent adhesion in the Z-direction, meaning that the printed layers do not detach - even with extreme deformation.\n\nPrinting Recommendations:\nApply Magigoo Flex to a clean build plate to improve adhesion." +## [filament:Ultrafuse TPC-45D @PG] +## inherits = Ultrafuse TPC-45D; *FLEXPG* + +## [filament:Ultrafuse TPC-45D @PG 0.6] +## inherits = Ultrafuse TPC-45D; *FLEX06PG* + +## [filament:Ultrafuse TPC-45D @PG 0.8] +## inherits = Ultrafuse TPC-45D; *FLEX08PG* + + ## [filament:Ultrafuse TPS-90A] ## inherits = Ultrafuse TPC-45D ## filament_density = 1.04 @@ -3569,6 +5908,15 @@ min_fan_speed = 20 max_fan_speed = 100 filament_notes = "Material Description\nUltrafuse® TPU 64D is the hardest elastomer in BASF Forward AM’s flexible productline. The material shows a relatively high rigidity while maintaining a certain flexibility. This filament is the perfect match for industrial applications requiring rigid parts being resistant to impact, wear and tear. Due to its property profile, the material can be used as an alternative for parts made from ABS and rubbers. Ultrafuse® TPU 64D is easy to print on direct drive and bowden style printers and is compatible with soluble BVOH support to realize the most complex geometries.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." +## [filament:Ultrafuse TPU-64D @PG] +## inherits = Ultrafuse TPU-64D; *FLEXPG* + +## [filament:Ultrafuse TPU-64D @PG 0.6] +## inherits = Ultrafuse TPU-64D; *FLEX06PG* + +## [filament:Ultrafuse TPU-64D @PG 0.8] +## inherits = Ultrafuse TPU-64D; *FLEX08PG* + [filament:Ultrafuse TPU-85A] inherits = Ultrafuse TPU-64D filament_density = 1.11 @@ -3576,6 +5924,22 @@ first_layer_temperature = 225 temperature = 220 filament_notes = "Material Description\nUltrafuse® TPU 85A comes in its natural white color. Chemical properties (e.g. resistance against particular substances) and tolerance for solvents can be made available, if these factors are relevant for a specific application. Generally, these properties correspond to publicly available data on polyether based TPUs. This material is not FDA conform. Good flexibility at low temperature, good wear performance and good damping behavior are the key features of Ultrafuse® TPU 85A.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." +[filament:Ultrafuse TPU-85A @PG] +inherits = Ultrafuse TPU-85A; *FLEXPG* +filament_max_volumetric_speed = 3 +extrusion_multiplier = 1.1 +first_layer_temperature = 220 +temperature = 215 +filament_retract_length = 3.5 + +[filament:Ultrafuse TPU-85A @PG 0.6] +inherits = Ultrafuse TPU-85A @PG; *FLEX06PG* +filament_max_volumetric_speed = 6 + +[filament:Ultrafuse TPU-85A @PG 0.8] +inherits = Ultrafuse TPU-85A @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + [filament:Ultrafuse TPU-95A] inherits = Ultrafuse TPU-85A filament_density = 1.14 @@ -3583,6 +5947,19 @@ first_layer_temperature = 230 temperature = 225 filament_notes = "Material Description\nUltrafuse® TPU 95A comes with a well-balanced profile of flexibility and durability. On top of that, it allows for easier and faster printing then softer TPU grades. Parts printed with Ultrafuse® TPU 95A show a high elongation, good impact resistance, excellent layer adhesion and a good resistance to oils and common industrially used chemicals. Due to its good printing behavior, Ultrafuse® TPU 95A is a good choice for starting printing flexible materials on both direct drive and bowden style printers.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." +[filament:Ultrafuse TPU-95A @PG] +inherits = Ultrafuse TPU-95A; *FLEXPG* +filament_max_volumetric_speed = 2.5 +filament_retract_length = 3 + +[filament:Ultrafuse TPU-95A @PG 0.6] +inherits = Ultrafuse TPU-95A @PG; *FLEX06PG* +filament_max_volumetric_speed = 5 + +[filament:Ultrafuse TPU-95A @PG 0.8] +inherits = Ultrafuse TPU-95A @PG; *FLEX08PG* +filament_max_volumetric_speed = 7 + [filament:Ultrafuse rPET] inherits = Ultrafuse PET filament_density = 1.27 @@ -3599,6 +5976,20 @@ filament_retract_length = 1.2 filament_retract_lift = 0.6 filament_wipe = nil +[filament:Ultrafuse rPET @PG] +inherits = Ultrafuse rPET; *PETPG* +filament_max_volumetric_speed = 8 + +[filament:Ultrafuse rPET @PG 0.6] +inherits = Ultrafuse rPET; *PET06PG* +filament_max_volumetric_speed = 16 + +[filament:Ultrafuse rPET @PG 0.8] +inherits = Ultrafuse rPET; *PET08PG* +filament_max_volumetric_speed = 18 +first_layer_temperature = 235 +temperature = 245 + [filament:Ultrafuse Metal] inherits = *ABSC* renamed_from = "Ultrafuse 17-4 PH" @@ -3617,10 +6008,20 @@ cooling = 0 fan_always_on = 0 filament_max_volumetric_speed = 4 filament_type = METAL -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material start_filament_gcode = "M900 K0" filament_colour = #FFFFFF +[filament:Ultrafuse Metal @PG] +inherits = Ultrafuse Metal; *ABSPG*; *04PLUSPG* +filament_max_volumetric_speed = 4 +start_filament_gcode = "M900 K0" + +[filament:Ultrafuse Metal @PG 0.6] +inherits = Ultrafuse Metal @PG; *ABS06PG* +filament_max_volumetric_speed = 4 +start_filament_gcode = "M900 K0" + [filament:Polymaker PC-Max] inherits = *ABS* filament_vendor = Polymaker @@ -3635,6 +6036,20 @@ temperature = 270 bridge_fan_speed = 0 filament_max_volumetric_speed = 8 +[filament:Polymaker PC-Max @PG] +inherits = Polymaker PC-Max; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_max_volumetric_speed = 8 + +[filament:Polymaker PC-Max @PG 0.6] +inherits = Polymaker PC-Max @PG; *ABS06PG* +filament_max_volumetric_speed = 12 + +[filament:Polymaker PC-Max @PG 0.8] +inherits = Polymaker PC-Max @PG; *ABS08PG* +filament_max_volumetric_speed = 15 + [filament:PrimaSelect PVA+] inherits = *PLA* filament_vendor = PrimaSelect @@ -3651,13 +6066,39 @@ first_layer_temperature = 195 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" temperature = 195 +[filament:PrimaSelect PVA+ @PG] +inherits = PrimaSelect PVA+; *PLAPG* +filament_max_volumetric_speed = 4 + +[filament:PrimaSelect PVA+ @PG 0.6] +inherits = PrimaSelect PVA+ @PG; *PLA06PG* +filament_max_volumetric_speed = 6 + +[filament:PrimaSelect PVA+ @PG 0.8] +inherits = PrimaSelect PVA+ @PG; *PLA08PG* +first_layer_temperature = 205 +temperature = 205 +filament_max_volumetric_speed = 9 + [filament:Prusa ABS] inherits = *ABSC* filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusa ABS @PG] +inherits = Prusa ABS; *ABSPG* +bed_temperature = 105 + +[filament:Prusa ABS @PG 0.6] +inherits = Prusa ABS @PG; *ABS06PG* + +[filament:Prusa ABS @PG 0.8] +inherits = Prusa ABS @PG; *ABS08PG* +first_layer_temperature = 265 +temperature = 265 [filament:*ABS MMU2*] inherits = Prusa ABS @@ -3870,6 +6311,7 @@ filament_cost = 27.82 filament_spool_weight = 230 [filament:Prusa HIPS] +## discontinued inherits = *ABS* filament_vendor = Made for Prusa filament_cost = 27.3 @@ -3888,7 +6330,7 @@ max_fan_speed = 20 min_fan_speed = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" temperature = 220 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Generic HIPS] inherits = *ABS* @@ -3908,7 +6350,17 @@ max_fan_speed = 20 min_fan_speed = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" temperature = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Generic HIPS @PG] +inherits = Generic HIPS; *ABSPG* +bed_temperature = 105 + +[filament:Generic HIPS @PG 0.6] +inherits = Generic HIPS @PG; *ABS06PG* + +[filament:Generic HIPS @PG 0.8] +inherits = Generic HIPS @PG; *ABS08PG* [filament:Prusa PETG] inherits = *PET* @@ -3917,7 +6369,16 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.27 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusa PETG @PG] +inherits = Prusa PETG; *PETPG* + +[filament:Prusa PETG @PG 0.6] +inherits = Prusa PETG; *PET06PG* + +[filament:Prusa PETG @PG 0.8] +inherits = Prusa PETG; *PET08PG* [filament:Verbatim PETG] inherits = *PET* @@ -3925,7 +6386,16 @@ filament_vendor = Verbatim filament_cost = 27.90 filament_density = 1.27 filament_spool_weight = 235 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Verbatim PETG @PG] +inherits = Verbatim PETG; *PETPG* + +[filament:Verbatim PETG @PG 0.6] +inherits = Verbatim PETG; *PET06PG* + +[filament:Verbatim PETG @PG 0.8] +inherits = Verbatim PETG; *PET08PG* [filament:Prusament PETG] inherits = *PET* @@ -3936,7 +6406,18 @@ filament_cost = 36.29 filament_density = 1.27 filament_spool_weight = 201 filament_type = PETG -compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusament PETG @PG] +inherits = Prusament PETG; *PETPG* + +[filament:Prusament PETG @PG 0.6] +inherits = Prusament PETG; *PET06PG* + +[filament:Prusament PETG @PG 0.8] +inherits = Prusament PETG; *PET08PG* +first_layer_temperature = 250 +temperature = 260 [filament:Prusament PETG Carbon Fiber] inherits = Prusament PETG @@ -3949,6 +6430,44 @@ filament_density = 1.27 filament_colour = #BBBBBB compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +[filament:Prusament PETG Carbon Fiber @PG] +inherits = Prusament PETG; *PETPG*; *04PLUSPG* + +[filament:Prusament PETG Carbon Fiber @PG 0.6] +inherits = Prusament PETG Carbon Fiber @PG; *PET06PG* + +[filament:Prusament PETG Carbon Fiber @PG 0.8] +inherits = Prusament PETG Carbon Fiber @PG; *PET08PG* + +[filament:Prusament PETG Tungsten 75%] +inherits = *PET* +filament_vendor = Prusa Polymers +filament_colour = #BBBBBB +first_layer_temperature = 255 +temperature = 265 +filament_cost = 277.09 +filament_density = 4 +filament_spool_weight = 201 +filament_type = PETG +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +cooling = 0 +min_fan_speed = 50 +max_fan_speed = 70 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.01{else}0{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif nozzle_diameter[0]==0.4}M900 K12{else}M900 K0{endif} ; Filament gcode LA 1.0" + +[filament:Prusament PETG Tungsten 75% @PG] +inherits = Prusament PETG Tungsten 75%; *PETPG* +filament_max_volumetric_speed = 8 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.01{else}0{endif} ; Filament gcode" + +[filament:Prusament PETG Tungsten 75% @PG 0.6] +inherits = Prusament PETG Tungsten 75% @PG; *PET06PG* +filament_max_volumetric_speed = 9 + +[filament:Prusament PETG Tungsten 75% @PG 0.8] +inherits = Prusament PETG Tungsten 75% @PG; *PET08PG* +filament_max_volumetric_speed = 10 + [filament:Prusa PETG @0.6 nozzle] inherits = *PET06* renamed_from = "Prusa PET 0.6 nozzle"; "Prusa PETG 0.6 nozzle" @@ -4116,7 +6635,17 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.24 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusa PLA @PG] +inherits = Prusa PLA; *PLAPG* + +[filament:Prusa PLA @PG 0.6] +inherits = Prusa PLA; *PLA06PG* +filament_max_volumetric_speed = 15.5 + +[filament:Prusa PLA @PG 0.8] +inherits = Prusa PLA; *PLA08PG* [filament:Eolas Prints PLA] inherits = *PLA* @@ -4128,17 +6657,44 @@ filament_colour = #4D9398 filament_max_volumetric_speed = 15 temperature = 208 +[filament:Eolas Prints PLA @PG] +inherits = Eolas Prints PLA; *PLAPG* + +[filament:Eolas Prints PLA @PG 0.6] +inherits = Eolas Prints PLA; *PLA06PG* + +[filament:Eolas Prints PLA @PG 0.8] +inherits = Eolas Prints PLA; *PLA08PG* + [filament:Eolas Prints PLA Matte] inherits = Eolas Prints PLA filament_cost = 25.50 filament_max_volumetric_speed = 14 temperature = 212 +[filament:Eolas Prints PLA Matte @PG] +inherits = Eolas Prints PLA Matte; *PLAPG* + +[filament:Eolas Prints PLA Matte @PG 0.6] +inherits = Eolas Prints PLA Matte; *PLA06PG* + +[filament:Eolas Prints PLA Matte @PG 0.8] +inherits = Eolas Prints PLA Matte; *PLA08PG* + [filament:Eolas Prints INGEO 850] inherits = Eolas Prints PLA filament_cost = 25.90 temperature = 210 +[filament:Eolas Prints INGEO 850 @PG] +inherits = Eolas Prints INGEO 850; *PLAPG* + +[filament:Eolas Prints INGEO 850 @PG 0.6] +inherits = Eolas Prints INGEO 850; *PLA06PG* + +[filament:Eolas Prints INGEO 850 @PG 0.8] +inherits = Eolas Prints INGEO 850; *PLA08PG* + [filament:Eolas Prints INGEO 870] inherits = Eolas Prints PLA filament_cost = 25.90 @@ -4147,6 +6703,15 @@ first_layer_bed_temperature = 68 first_layer_temperature = 220 bed_temperature = 65 +[filament:Eolas Prints INGEO 870 @PG] +inherits = Eolas Prints INGEO 870; *PLAPG* + +[filament:Eolas Prints INGEO 870 @PG 0.6] +inherits = Eolas Prints INGEO 870; *PLA06PG* + +[filament:Eolas Prints INGEO 870 @PG 0.8] +inherits = Eolas Prints INGEO 870; *PLA08PG* + [filament:Eolas Prints PETG] inherits = *PET* filament_vendor = Eolas Prints @@ -4162,6 +6727,15 @@ bed_temperature = 90 filament_retract_length = 1.1 filament_retract_lift = 0.22 +[filament:Eolas Prints PETG @PG] +inherits = Eolas Prints PETG; *PETPG* + +[filament:Eolas Prints PETG @PG 0.6] +inherits = Eolas Prints PETG; *PET06PG* + +[filament:Eolas Prints PETG @PG 0.8] +inherits = Eolas Prints PETG; *PET08PG* + [filament:Eolas Prints PETG @MINI] inherits = Eolas Prints PETG; *PETMINI* @@ -4171,6 +6745,15 @@ filament_cost = 35.90 temperature = 237 first_layer_temperature = 232 +[filament:Eolas Prints PETG - UV Resistant @PG] +inherits = Eolas Prints PETG - UV Resistant; *PETPG* + +[filament:Eolas Prints PETG - UV Resistant @PG 0.6] +inherits = Eolas Prints PETG - UV Resistant; *PET06PG* + +[filament:Eolas Prints PETG - UV Resistant @PG 0.8] +inherits = Eolas Prints PETG - UV Resistant; *PET08PG* + [filament:Eolas Prints PETG - UV Resistant @MINI] inherits = Eolas Prints PETG - UV Resistant; *PETMINI* @@ -4187,6 +6770,17 @@ bed_temperature = 30 filament_retract_length = 0 extrusion_multiplier = 1.16 +[filament:Eolas Prints TPU 93A @PG] +inherits = Eolas Prints TPU 93A; *FLEXPG* +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 + +[filament:Eolas Prints TPU 93A @PG 0.6] +inherits = Eolas Prints TPU 93A @PG; *FLEX06PG* + +[filament:Eolas Prints TPU 93A @PG 0.8] +inherits = Eolas Prints TPU 93A @PG; *FLEX08PG* + [filament:Print With Smile PLA] inherits = *PLA* filament_vendor = Print With Smile @@ -4198,6 +6792,17 @@ filament_max_volumetric_speed = 15 first_layer_temperature = 205 temperature = 205 +[filament:Print With Smile PLA @PG] +inherits = Print With Smile PLA; *PLAPG* + +[filament:Print With Smile PLA @PG 0.6] +inherits = Print With Smile PLA; *PLA06PG* + +[filament:Print With Smile PLA @PG 0.8] +inherits = Print With Smile PLA; *PLA08PG* +first_layer_temperature = 215 +temperature = 215 + [filament:Print With Smile PETG] inherits = *PET* filament_vendor = Print With Smile @@ -4211,6 +6816,18 @@ first_layer_bed_temperature = 85 first_layer_temperature = 240 bed_temperature = 90 +[filament:Print With Smile PETG @PG] +inherits = Print With Smile PETG; *PETPG* +filament_max_volumetric_speed = 8 + +[filament:Print With Smile PETG @PG 0.6] +inherits = Print With Smile PETG; *PET06PG* +filament_max_volumetric_speed = 16 + +[filament:Print With Smile PETG @PG 0.8] +inherits = Print With Smile PETG; *PET08PG* +filament_max_volumetric_speed = 19 + [filament:Print With Smile PETG @MINI] inherits = Print With Smile PETG; *PETMINI* @@ -4230,6 +6847,19 @@ filament_max_volumetric_speed = 11 [filament:Print With Smile ASA @MINI] inherits = Print With Smile ASA; *ABSMINI* +[filament:Print With Smile ASA @PG] +inherits = Print With Smile ASA; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:Print With Smile ASA @PG 0.6] +inherits = Print With Smile ASA @PG; *ABS06PG* + +[filament:Print With Smile ASA @PG 0.8] +inherits = Print With Smile ASA @PG; *ABS08PG* +first_layer_temperature = 255 +temperature = 255 + [filament:Print With Smile ABS] inherits = *ABSC* filament_vendor = Print With Smile @@ -4241,6 +6871,17 @@ temperature = 240 [filament:Print With Smile ABS @MINI] inherits = Print With Smile ABS; *ABSMINI* +[filament:Print With Smile ABS @PG] +inherits = Print With Smile ABS; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:Print With Smile ABS @PG 0.6] +inherits = Print With Smile ABS @PG; *ABS06PG* + +[filament:Print With Smile ABS @PG 0.8] +inherits = Print With Smile ABS @PG; *ABS08PG* + [filament:Print With Smile PETG CF] inherits = Extrudr XPETG CF filament_vendor = Print With Smile @@ -4258,6 +6899,18 @@ filament_max_volumetric_speed = 5 [filament:Print With Smile PETG CF @MINI] inherits = Print With Smile PETG CF; *PETMINI* +[filament:Print With Smile PETG CF @PG] +inherits = Print With Smile PETG CF; *PETPG* +filament_max_volumetric_speed = 5 + +[filament:Print With Smile PETG CF @PG 0.6] +inherits = Print With Smile PETG CF @PG; *PET06PG* +filament_max_volumetric_speed = 6.5 + +[filament:Print With Smile PETG CF @PG 0.8] +inherits = Print With Smile PETG CF @PG; *PET08PG* +filament_max_volumetric_speed = 8 + [filament:Print With Smile TPU96A] inherits = *FLEX* filament_vendor = Print With Smile @@ -4274,6 +6927,19 @@ full_fan_speed_layer = 6 filament_retract_length = 1.2 filament_deretract_speed = 20 +[filament:Print With Smile TPU96A @PG] +inherits = Print With Smile TPU96A; *FLEXPG* +filament_retract_length = 2 +filament_max_volumetric_speed = 3 + +[filament:Print With Smile TPU96A @PG 0.6] +inherits = Print With Smile TPU96A @PG; *FLEX06PG* +filament_max_volumetric_speed = 5 + +[filament:Print With Smile TPU96A @PG 0.8] +inherits = Print With Smile TPU96A @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + [filament:Fiberlogy Easy PLA] inherits = *PLA* renamed_from = Fiberlogy PLA @@ -4284,6 +6950,15 @@ first_layer_temperature = 220 temperature = 220 filament_spool_weight = 330 +[filament:Fiberlogy Easy PLA @PG] +inherits = Fiberlogy Easy PLA; *PLAPG* + +[filament:Fiberlogy Easy PLA @PG 0.6] +inherits = Fiberlogy Easy PLA; *PLA06PG* + +[filament:Fiberlogy Easy PLA @PG 0.8] +inherits = Fiberlogy Easy PLA; *PLA08PG* + [filament:Fiberlogy Easy PET-G] inherits = *PET* renamed_from = Fiberlogy PETG @@ -4291,7 +6966,7 @@ filament_vendor = Fiberlogy filament_spool_weight = 330 filament_cost = 20 filament_density = 1.27 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material first_layer_bed_temperature = 80 bed_temperature = 80 first_layer_temperature = 235 @@ -4303,6 +6978,15 @@ disable_fan_first_layers = 5 full_fan_speed_layer = 5 slowdown_below_layer_time = 15 +[filament:Fiberlogy Easy PET-G @PG] +inherits = Fiberlogy Easy PET-G; *PETPG* + +[filament:Fiberlogy Easy PET-G @PG 0.6] +inherits = Fiberlogy Easy PET-G; *PET06PG* + +[filament:Fiberlogy Easy PET-G @PG 0.8] +inherits = Fiberlogy Easy PET-G; *PET08PG* + [filament:Fiberlogy ASA] inherits = *ABS* filament_vendor = Fiberlogy @@ -4324,6 +7008,17 @@ filament_type = ASA fan_below_layer_time = 30 disable_fan_first_layers = 5 +[filament:Fiberlogy ASA @PG] +inherits = Fiberlogy ASA; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:Fiberlogy ASA @PG 0.6] +inherits = Fiberlogy ASA @PG; *ABS06PG* + +[filament:Fiberlogy ASA @PG 0.8] +inherits = Fiberlogy ASA @PG; *ABS08PG* + [filament:Fiberlogy ASA @MINI] inherits = Fiberlogy ASA; *ABSMINI* @@ -4345,6 +7040,15 @@ filament_type = ABS fan_below_layer_time = 25 disable_fan_first_layers = 5 +[filament:Fiberlogy Easy ABS @PG] +inherits = Fiberlogy Easy ABS; *ABSPG* + +[filament:Fiberlogy Easy ABS @PG 0.6] +inherits = Fiberlogy Easy ABS; *ABS06PG* + +[filament:Fiberlogy Easy ABS @PG 0.8] +inherits = Fiberlogy Easy ABS; *ABS08PG* + [filament:Fiberlogy Easy ABS @MINI] inherits = Fiberlogy Easy ABS; *ABSMINI* @@ -4370,6 +7074,17 @@ fan_below_layer_time = 20 slowdown_below_layer_time = 15 disable_fan_first_layers = 5 +[filament:Fiberlogy CPE HT @PG] +inherits = Fiberlogy CPE HT; *PETPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 + +[filament:Fiberlogy CPE HT @PG 0.6] +inherits = Fiberlogy CPE HT @PG; *PET06PG* + +[filament:Fiberlogy CPE HT @PG 0.8] +inherits = Fiberlogy CPE HT @PG; *PET08PG* + [filament:Fiberlogy PCTG] inherits = Fiberlogy CPE HT filament_cost = 29.41 @@ -4383,6 +7098,15 @@ first_layer_bed_temperature = 90 bed_temperature = 90 filament_type = PCTG +[filament:Fiberlogy PCTG @PG] +inherits = Fiberlogy PCTG; *PETPG* + +[filament:Fiberlogy PCTG @PG 0.6] +inherits = Fiberlogy PCTG; *PET06PG* + +[filament:Fiberlogy PCTG @PG 0.8] +inherits = Fiberlogy PCTG; *PET08PG* + [filament:Fiberlogy PCTG @MINI] inherits = Fiberlogy PCTG; *PETMINI* @@ -4413,6 +7137,22 @@ min_print_speed = 15 cooling = 1 filament_spool_weight = 330 +[filament:Fiberlogy FiberFlex 40D @PG] +inherits = Fiberlogy FiberFlex 40D; *FLEXPG* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.2 +extrusion_multiplier = 1.1 +first_layer_temperature = 220 +temperature = 220 + +[filament:Fiberlogy FiberFlex 40D @PG 0.6] +inherits = Fiberlogy FiberFlex 40D @PG; *FLEX06PG* +filament_max_volumetric_speed = 6 + +[filament:Fiberlogy FiberFlex 40D @PG 0.8] +inherits = Fiberlogy FiberFlex 40D @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:Fiberlogy FiberFlex 40D @MINI] inherits = *FLEXMINI* filament_vendor = Fiberlogy @@ -4442,6 +7182,19 @@ filament_retract_before_travel = 2 filament_cost = 49.11 filament_retract_length = 1.2 +[filament:Fiberlogy MattFlex 40D @PG] +inherits = Fiberlogy MattFlex 40D; *FLEXPG* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.2 + +[filament:Fiberlogy MattFlex 40D @PG 0.6] +inherits = Fiberlogy MattFlex 40D @PG; *FLEX06PG* +filament_max_volumetric_speed = 6 + +[filament:Fiberlogy MattFlex 40D @PG 0.8] +inherits = Fiberlogy MattFlex 40D @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:Fiberlogy FiberFlex 30D] inherits = Fiberlogy FiberFlex 40D filament_max_volumetric_speed = 1.2 @@ -4453,6 +7206,24 @@ max_fan_speed = 60 filament_density = 1.07 filament_retract_length = 1.2 +[filament:Fiberlogy FiberFlex 30D @PG] +inherits = Fiberlogy FiberFlex 30D; *FLEXPG* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3 +first_layer_temperature = 220 +temperature = 220 +first_layer_bed_temperature = 55 +bed_temperature = 55 +extrusion_multiplier = 1.1 + +[filament:Fiberlogy FiberFlex 30D @PG 0.6] +inherits = Fiberlogy FiberFlex 30D @PG; *FLEX06PG* +filament_max_volumetric_speed = 7 + +[filament:Fiberlogy FiberFlex 30D @PG 0.8] +inherits = Fiberlogy FiberFlex 30D @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:Fiberlogy FiberSatin] inherits = Fiberlogy Easy PLA first_layer_temperature = 215 @@ -4461,6 +7232,15 @@ extrusion_multiplier = 1.03 filament_density = 1.2 filament_cost = 32.35 +[filament:Fiberlogy FiberSatin @PG] +inherits = Fiberlogy FiberSatin; *PLAPG* + +[filament:Fiberlogy FiberSatin @PG 0.6] +inherits = Fiberlogy FiberSatin; *PLA06PG* + +[filament:Fiberlogy FiberSatin @PG 0.8] +inherits = Fiberlogy FiberSatin; *PLA08PG* + [filament:Fiberlogy FiberSilk] inherits = Fiberlogy FiberSatin first_layer_temperature = 230 @@ -4469,6 +7249,15 @@ extrusion_multiplier = 0.97 filament_density = 1.22 filament_cost = 32.35 +[filament:Fiberlogy FiberSilk @PG] +inherits = Fiberlogy FiberSilk; *PLAPG* + +[filament:Fiberlogy FiberSilk @PG 0.6] +inherits = Fiberlogy FiberSilk; *PLA06PG* + +[filament:Fiberlogy FiberSilk @PG 0.8] +inherits = Fiberlogy FiberSilk; *PLA08PG* + [filament:Fiberlogy FiberWood] inherits = Fiberlogy Easy PLA first_layer_temperature = 185 @@ -4478,6 +7267,20 @@ filament_density = 1.23 filament_cost = 38.66 filament_max_volumetric_speed = 8 +[filament:Fiberlogy FiberWood @PG] +inherits = Fiberlogy FiberWood; *PLAPG* +filament_max_volumetric_speed = 8 + +[filament:Fiberlogy FiberWood @PG 0.6] +inherits = Fiberlogy FiberWood; *PLA06PG* +filament_max_volumetric_speed = 12 + +[filament:Fiberlogy FiberWood @PG 0.8] +inherits = Fiberlogy FiberWood; *PLA08PG* +filament_max_volumetric_speed = 15 +first_layer_temperature = 195 +temperature = 195 + [filament:Fiberlogy HD PLA] inherits = Fiberlogy Easy PLA first_layer_temperature = 230 @@ -4486,6 +7289,15 @@ extrusion_multiplier = 1 filament_density = 1.24 filament_cost = 30.59 +[filament:Fiberlogy HD PLA @PG] +inherits = Fiberlogy HD PLA; *PLAPG* + +[filament:Fiberlogy HD PLA @PG 0.6] +inherits = Fiberlogy HD PLA; *PLA06PG* + +[filament:Fiberlogy HD PLA @PG 0.8] +inherits = Fiberlogy HD PLA; *PLA08PG* + [filament:Fiberlogy PLA Mineral] inherits = Fiberlogy Easy PLA first_layer_temperature = 195 @@ -4495,11 +7307,34 @@ filament_density = 1.38 filament_cost = 37.64 filament_max_volumetric_speed = 10 +[filament:Fiberlogy PLA Mineral @PG] +inherits = Fiberlogy PLA Mineral; *PLAPG* +filament_max_volumetric_speed = 10 + +[filament:Fiberlogy PLA Mineral @PG 0.6] +inherits = Fiberlogy PLA Mineral; *PLA06PG* +filament_max_volumetric_speed = 12 + +[filament:Fiberlogy PLA Mineral @PG 0.8] +inherits = Fiberlogy PLA Mineral; *PLA08PG* +filament_max_volumetric_speed = 14 +first_layer_temperature = 200 +temperature = 200 + [filament:Fiberlogy Impact PLA] inherits = Fiberlogy HD PLA filament_density = 1.22 filament_cost = 27.65 +[filament:Fiberlogy Impact PLA @PG] +inherits = Fiberlogy Impact PLA; *PLAPG* + +[filament:Fiberlogy Impact PLA @PG 0.6] +inherits = Fiberlogy Impact PLA; *PLA06PG* + +[filament:Fiberlogy Impact PLA @PG 0.8] +inherits = Fiberlogy Impact PLA; *PLA08PG* + [filament:Fiberlogy Nylon PA12] inherits = Fiberlogy ASA filament_type = PA @@ -4518,6 +7353,20 @@ filament_retract_lift = 0.2 filament_max_volumetric_speed = 6 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K26{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Fiberlogy Nylon PA12 @PG] +inherits = Fiberlogy Nylon PA12; *ABSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_max_volumetric_speed = 6 + +[filament:Fiberlogy Nylon PA12 @PG 0.6] +inherits = Fiberlogy Nylon PA12 @PG; *ABS06PG* +filament_max_volumetric_speed = 8 + +[filament:Fiberlogy Nylon PA12 @PG 0.8] +inherits = Fiberlogy Nylon PA12 @PG; *ABS08PG* +filament_max_volumetric_speed = 11 + [filament:Fiberlogy Nylon PA12+CF15] inherits = Fiberlogy Nylon PA12 extrusion_multiplier = 0.97 @@ -4533,13 +7382,39 @@ fan_below_layer_time = 20 bridge_fan_speed = 30 fan_always_on = 0 filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Fiberlogy Nylon PA12+CF15 @PG] +inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *04PLUSPG* +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_max_volumetric_speed = 8 + +[filament:Fiberlogy Nylon PA12+CF15 @PG 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS06PG* +filament_max_volumetric_speed = 10 + +[filament:Fiberlogy Nylon PA12+CF15 @PG 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS08PG* +filament_max_volumetric_speed = 12 [filament:Fiberlogy Nylon PA12+GF15] inherits = Fiberlogy Nylon PA12+CF15 filament_density = 1.13 filament_max_volumetric_speed = 8 +[filament:Fiberlogy Nylon PA12+GF15 @PG] +inherits = Fiberlogy Nylon PA12+CF15 @PG +filament_density = 1.13 + +[filament:Fiberlogy Nylon PA12+GF15 @PG 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @PG 0.6 +filament_density = 1.13 + +[filament:Fiberlogy Nylon PA12+GF15 @PG 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @PG 0.8 +filament_density = 1.13 + [filament:Fiberlogy PP] inherits = *ABS* filament_vendor = Fiberlogy @@ -4563,6 +7438,20 @@ fan_below_layer_time = 100 disable_fan_first_layers = 5 filament_max_volumetric_speed = 5 +[filament:Fiberlogy PP @PG] +inherits = Fiberlogy PP; *ABSPG* +filament_max_volumetric_speed = 5 + +[filament:Fiberlogy PP @PG 0.6] +inherits = Fiberlogy PP @PG; *ABS06PG* +filament_max_volumetric_speed = 7 + +[filament:Fiberlogy PP @PG 0.8] +inherits = Fiberlogy PP @PG; *ABS08PG* +filament_max_volumetric_speed = 10 +first_layer_temperature = 250 +temperature = 250 + [filament:Filament PM PLA] inherits = *PLA* renamed_from = "Plasty Mladec PLA" @@ -4571,12 +7460,35 @@ filament_cost = 27.82 filament_density = 1.24 filament_spool_weight = 230 +[filament:Filament PM PLA @PG] +inherits = Filament PM PLA; *PLAPG* + +[filament:Filament PM PLA @PG 0.6] +inherits = Filament PM PLA; *PLA06PG* +filament_max_volumetric_speed = 15 + +[filament:Filament PM PLA @PG 0.8] +inherits = Filament PM PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:AmazonBasics PLA] inherits = *PLA* filament_vendor = AmazonBasics filament_cost = 25.4 filament_density = 1.24 +[filament:AmazonBasics PLA @PG] +inherits = AmazonBasics PLA; *PLAPG* + +[filament:AmazonBasics PLA @PG 0.6] +inherits = AmazonBasics PLA; *PLA06PG* + +[filament:AmazonBasics PLA @PG 0.8] +inherits = AmazonBasics PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Overture PLA] inherits = *PLA* filament_vendor = Overture @@ -4584,6 +7496,17 @@ filament_cost = 22 filament_density = 1.24 filament_spool_weight = 235 +[filament:Overture PLA @PG] +inherits = Overture PLA; *PLAPG* + +[filament:Overture PLA @PG 0.6] +inherits = Overture PLA; *PLA06PG* + +[filament:Overture PLA @PG 0.8] +inherits = Overture PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Hatchbox PLA] inherits = *PLA* filament_vendor = Hatchbox @@ -4591,6 +7514,17 @@ filament_cost = 25.4 filament_density = 1.27 filament_spool_weight = 245 +[filament:Hatchbox PLA @PG] +inherits = Hatchbox PLA; *PLAPG* + +[filament:Hatchbox PLA @PG 0.6] +inherits = Hatchbox PLA; *PLA06PG* + +[filament:Hatchbox PLA @PG 0.8] +inherits = Hatchbox PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Esun PLA] inherits = *PLA* filament_vendor = Esun @@ -4598,24 +7532,68 @@ filament_cost = 25.4 filament_density = 1.24 filament_spool_weight = 265 +[filament:Esun PLA @PG] +inherits = Esun PLA; *PLAPG* + +[filament:Esun PLA @PG 0.6] +inherits = Esun PLA; *PLA06PG* + +[filament:Esun PLA @PG 0.8] +inherits = Esun PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Das Filament PLA] inherits = *PLA* filament_vendor = Das Filament filament_cost = 25.4 filament_density = 1.24 +[filament:Das Filament PLA @PG] +inherits = Das Filament PLA; *PLAPG* + +[filament:Das Filament PLA @PG 0.6] +inherits = Das Filament PLA; *PLA06PG* + +[filament:Das Filament PLA @PG 0.8] +inherits = Das Filament PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:EUMAKERS PLA] inherits = *PLA* filament_vendor = EUMAKERS filament_cost = 25.4 filament_density = 1.24 +[filament:EUMAKERS PLA @PG] +inherits = EUMAKERS PLA; *PLAPG* + +[filament:EUMAKERS PLA @PG 0.6] +inherits = EUMAKERS PLA; *PLA06PG* + +[filament:EUMAKERS PLA @PG 0.8] +inherits = EUMAKERS PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Floreon3D PLA] inherits = *PLA* filament_vendor = Floreon3D filament_cost = 25.4 filament_density = 1.24 +[filament:Floreon3D PLA @PG] +inherits = Floreon3D PLA; *PLAPG* + +[filament:Floreon3D PLA @PG 0.6] +inherits = Floreon3D PLA; *PLA06PG* + +[filament:Floreon3D PLA @PG 0.8] +inherits = Floreon3D PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Prusament PLA] inherits = *PLA* filament_vendor = Prusa Polymers @@ -4624,7 +7602,19 @@ filament_cost = 36.29 filament_density = 1.24 filament_spool_weight = 201 filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa" -compatible_printers_condition = nozzle_diameter[0]!=0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Prusament PLA @PG] +inherits = Prusament PLA; *PLAPG* + +[filament:Prusament PLA @PG 0.6] +inherits = Prusament PLA; *PLA06PG* +filament_max_volumetric_speed = 16 + +[filament:Prusament PLA @PG 0.8] +inherits = Prusament PLA; *PLA08PG* +first_layer_temperature = 225 +temperature = 225 [filament:Prusament PVB] inherits = *PLA* @@ -4639,10 +7629,21 @@ filament_max_volumetric_speed = 8 filament_type = PVB filament_soluble = 1 filament_colour = #FFFF6F -compatible_printers_condition = nozzle_diameter[0]!=0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material slowdown_below_layer_time = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Prusament PVB @PG] +inherits = Prusament PVB; *PLAPG* + +[filament:Prusament PVB @PG 0.6] +inherits = Prusament PVB; *PLA06PG* + +[filament:Prusament PVB @PG 0.8] +inherits = Prusament PVB; *PLA08PG* +first_layer_temperature = 225 +temperature = 225 + [filament:*PLA MMU2*] inherits = Prusa PLA compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.25 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material @@ -4709,6 +7710,7 @@ filament_density = 1.24 filament_spool_weight = 230 [filament:SemiFlex] +## discontinued inherits = *FLEX* renamed_from = "SemiFlex or Flexfill 98A" filament_vendor = Generic @@ -4739,6 +7741,20 @@ full_fan_speed_layer = 6 filament_retract_length = 1.2 filament_deretract_speed = 20 +[filament:Fillamentum Flexfill 98A @PG] +inherits = Fillamentum Flexfill 98A; *FLEXPG* +filament_max_volumetric_speed = 3 +filament_retract_length = 3 +extrusion_multiplier = 1.08 + +[filament:Fillamentum Flexfill 98A @PG 0.6] +inherits = Fillamentum Flexfill 98A @PG; *FLEX06PG* +filament_max_volumetric_speed = 5 + +[filament:Fillamentum Flexfill 98A @PG 0.8] +inherits = Fillamentum Flexfill 98A @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + [filament:ColorFabb VarioShore TPU] inherits = Fillamentum Flexfill 98A filament_vendor = ColorFabb @@ -4750,6 +7766,10 @@ extrusion_multiplier = 0.85 first_layer_temperature = 220 temperature = 220 +[filament:ColorFabb VarioShore TPU @PG] +inherits = ColorFabb VarioShore TPU; *FLEXPG* +filament_max_volumetric_speed = 1.5 + [filament:Taulman Bridge] inherits = *common* filament_vendor = Taulman @@ -4771,7 +7791,22 @@ temperature = 260 max_fan_speed = 0 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Taulman Bridge @PG] +inherits = Taulman Bridge; *ABSPG* +bed_temperature = 105 +filament_max_volumetric_speed = 7 + +[filament:Taulman Bridge @PG 0.6] +inherits = Taulman Bridge @PG; *ABS06PG* +filament_max_volumetric_speed = 9 + +[filament:Taulman Bridge @PG 0.8] +inherits = Taulman Bridge @PG; *ABS08PG* +filament_max_volumetric_speed = 12 +first_layer_temperature = 270 +temperature = 270 [filament:Taulman Bridge @MINI] inherits = Taulman Bridge @@ -4806,6 +7841,18 @@ min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.1{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K28{elsif nozzle_diameter[0]==0.8};{else}M900 K48{endif} ; Filament gcode LA 1.0" temperature = 250 +[filament:Fillamentum Nylon FX256 @PG] +inherits = Fillamentum Nylon FX256; *PAPG* +filament_max_volumetric_speed = 6 + +[filament:Fillamentum Nylon FX256 @PG 0.6] +inherits = Fillamentum Nylon FX256 @PG; *PA06PG* +filament_max_volumetric_speed = 8 + +[filament:Fillamentum Nylon FX256 @PG 0.8] +inherits = Fillamentum Nylon FX256 @PG; *PA08PG* +filament_max_volumetric_speed = 11 + [filament:Fiberthree F3 PA Pure Pro] inherits = *common* filament_vendor = Fiberthree @@ -4834,7 +7881,19 @@ filament_retract_speed = 40 filament_retract_lift = nil filament_retract_before_travel = 1.5 filament_wipe = 0 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA Pure Pro @PG] +inherits = Fiberthree F3 PA Pure Pro; *PAPG* +filament_max_volumetric_speed = 5 + +[filament:Fiberthree F3 PA Pure Pro @PG 0.6] +inherits = Fiberthree F3 PA Pure Pro @PG; *PA06PG* +filament_max_volumetric_speed = 7 + +[filament:Fiberthree F3 PA Pure Pro @PG 0.8] +inherits = Fiberthree F3 PA Pure Pro @PG; *PA08PG* +filament_max_volumetric_speed = 10 [filament:Fiberthree F3 PA-CF Pro] inherits = *common* @@ -4864,7 +7923,19 @@ filament_retract_speed = 40 filament_retract_lift = nil filament_retract_before_travel = 1.5 filament_wipe = 0 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-CF Pro @PG] +inherits = Fiberthree F3 PA-CF Pro; *PAPG* +filament_max_volumetric_speed = 5 + +[filament:Fiberthree F3 PA-CF Pro @PG 0.6] +inherits = Fiberthree F3 PA-CF Pro; *PA06PG* +filament_max_volumetric_speed = 7 + +[filament:Fiberthree F3 PA-CF Pro @PG 0.8] +inherits = Fiberthree F3 PA-CF Pro; *PA08PG* +filament_max_volumetric_speed = 10 [filament:Fiberthree F3 PA-GF Pro] inherits = Fiberthree F3 PA-CF Pro @@ -4875,6 +7946,18 @@ fan_always_on = 1 max_fan_speed = 15 min_fan_speed = 15 +[filament:Fiberthree F3 PA-GF Pro @PG] +inherits = Fiberthree F3 PA-GF Pro; *PAPG* +filament_max_volumetric_speed = 5 + +[filament:Fiberthree F3 PA-GF Pro @PG 0.6] +inherits = Fiberthree F3 PA-GF Pro @PG; *PA06PG* +filament_max_volumetric_speed = 7 + +[filament:Fiberthree F3 PA-GF Pro @PG 0.8] +inherits = Fiberthree F3 PA-GF Pro @PG; *PA08PG* +filament_max_volumetric_speed = 10 + [filament:Fiberthree F3 PA-GF30 Pro] inherits = Prusament PC Blend Carbon Fiber filament_vendor = Fiberthree @@ -4892,6 +7975,18 @@ min_fan_speed = 15 filament_type = PA filament_max_volumetric_speed = 6 +[filament:Fiberthree F3 PA-GF30 Pro @PG] +inherits = Fiberthree F3 PA-GF30 Pro; *PAPG* +filament_max_volumetric_speed = 6 + +[filament:Fiberthree F3 PA-GF30 Pro @PG 0.6] +inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA06PG* +filament_max_volumetric_speed = 7.5 + +[filament:Fiberthree F3 PA-GF30 Pro @PG 0.8] +inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA08PG* +filament_max_volumetric_speed = 10 + [filament:Taulman T-Glase] inherits = *PET* filament_vendor = Taulman @@ -4906,6 +8001,15 @@ max_fan_speed = 5 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" +[filament:Taulman T-Glase @PG] +inherits = Taulman T-Glase; *PAPG* + +[filament:Taulman T-Glase @PG 0.6] +inherits = Taulman T-Glase @PG; *PA06PG* + +[filament:Taulman T-Glase @PG 0.8] +inherits = Taulman T-Glase @PG; *PA08PG* + [filament:Verbatim PLA] inherits = *PLA* filament_vendor = Verbatim @@ -4913,6 +8017,17 @@ filament_cost = 42.99 filament_density = 1.24 filament_spool_weight = 235 +[filament:Verbatim PLA @PG] +inherits = Verbatim PLA; *PLAPG* + +[filament:Verbatim PLA @PG 0.6] +inherits = Verbatim PLA; *PLA06PG* + +[filament:Verbatim PLA @PG 0.8] +inherits = Verbatim PLA; *PLA08PG* +first_layer_temperature = 220 +temperature = 220 + [filament:Verbatim BVOH] inherits = *common* filament_vendor = Verbatim @@ -4937,6 +8052,18 @@ min_fan_speed = 100 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" temperature = 210 +[filament:Verbatim BVOH @PG] +inherits = Verbatim BVOH; *ABSPG* +filament_max_volumetric_speed = 4 + +[filament:Verbatim BVOH @PG 0.6] +inherits = Verbatim BVOH @PG; *ABS06PG* +filament_max_volumetric_speed = 6 + +[filament:Verbatim BVOH @PG 0.8] +inherits = Verbatim BVOH @PG; *ABS08PG* +filament_max_volumetric_speed = 9 + [filament:Verbatim BVOH @MMU2] inherits = Verbatim BVOH filament_vendor = Verbatim @@ -5018,6 +8145,18 @@ min_fan_speed = 100 start_filament_gcode = "M900 K0 ; Filament gcode" temperature = 220 +[filament:Verbatim PP @PG] +inherits = Verbatim PP; *ABSPG* +filament_max_volumetric_speed = 5 + +[filament:Verbatim PP @PG 0.6] +inherits = Verbatim PP @PG; *ABS06PG* +filament_max_volumetric_speed = 7 + +[filament:Verbatim PP @PG 0.8] +inherits = Verbatim PP @PG; *ABS08PG* +filament_max_volumetric_speed = 10 + [filament:FormFutura Centaur PP] inherits = *common* filament_vendor = FormFutura @@ -5042,7 +8181,21 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 235 filament_wipe = 0 filament_retract_lift = 0 -compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material + +[filament:FormFutura Centaur PP @PG] +inherits = FormFutura Centaur PP; *PETPG* +filament_max_volumetric_speed = 4 + +[filament:FormFutura Centaur PP @PG 0.6] +inherits = FormFutura Centaur PP; *PET06PG* +filament_max_volumetric_speed = 6 + +[filament:FormFutura Centaur PP @PG 0.8] +inherits = FormFutura Centaur PP; *PET08PG* +filament_max_volumetric_speed = 8 +first_layer_temperature = 240 +temperature = 240 [filament:FormFutura Centaur PP @MINI] inherits = FormFutura Centaur PP @@ -5284,6 +8437,13 @@ filament_cost = 54.99 filament_colour = #BBBBBB compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +[filament:Prusament PETG Tungsten 75% @MINI] +inherits = Prusament PETG Tungsten 75%; *PETMINI* +full_fan_speed_layer = 5 +start_filament_gcode = "M900 K0" +filament_colour = #BBBBBB +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" + [filament:Kimya PETG Carbon @MINI] inherits = Kimya PETG Carbon; *PETMINI* filament_max_volumetric_speed = 6 @@ -5537,14 +8697,14 @@ first_layer_temperature = 220 temperature = 220 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material [filament:Generic ABS @0.8 nozzle] inherits = Generic ABS first_layer_temperature = 265 temperature = 265 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Generic PETG @0.8 nozzle] inherits = Generic PETG @@ -5553,7 +8713,7 @@ temperature = 250 filament_max_volumetric_speed = 20 filament_retract_lift = 0.2 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusa PLA @0.8 nozzle] inherits = Prusa PLA @@ -5561,7 +8721,7 @@ first_layer_temperature = 220 temperature = 220 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusa PETG @0.8 nozzle] inherits = Prusa PETG @@ -5569,26 +8729,26 @@ first_layer_temperature = 240 temperature = 250 filament_max_volumetric_speed = 20 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusa ABS @0.8 nozzle] inherits = Prusa ABS first_layer_temperature = 265 temperature = 265 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Generic FLEX @0.8 nozzle] inherits = Generic FLEX filament_max_volumetric_speed = 4.3 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Generic HIPS @0.8 nozzle] inherits = Generic HIPS first_layer_temperature = 240 temperature = 240 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PLA @0.8 nozzle] inherits = Prusament PLA @@ -5596,7 +8756,7 @@ first_layer_temperature = 225 temperature = 225 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PETG @0.8 nozzle] inherits = Prusament PETG @@ -5605,7 +8765,7 @@ temperature = 260 filament_max_volumetric_speed = 20 filament_retract_lift = 0.2 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PETG Carbon Fiber @0.8 nozzle] inherits = Prusament PETG @0.8 nozzle @@ -5621,64 +8781,64 @@ first_layer_temperature = 265 temperature = 265 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PC Blend @0.8 nozzle] inherits = Prusament PC Blend filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PC Blend Carbon Fiber @0.8 nozzle] inherits = Prusament PC Blend Carbon Fiber filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PA11 Carbon Fiber @0.8 nozzle] inherits = Prusament PA11 Carbon Fiber filament_max_volumetric_speed = 11 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material [filament:Prusament PA11 Carbon Fiber @0.8 nozzle MK2] inherits = Prusament PA11 Carbon Fiber @MK2 filament_max_volumetric_speed = 11 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="XL" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) [filament:Prusament PC Blend @0.8 nozzle MK2] inherits = Prusament PC Blend @MK2 filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="XL" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) [filament:Prusament PVB @0.8 nozzle] inherits = Prusament PVB first_layer_temperature = 225 temperature = 225 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material slowdown_below_layer_time = 20 ## Filaments 0.8 nozzle MMU2 [filament:Generic HIPS @MMU2 0.8 nozzle] inherits = Generic HIPS @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material [filament:Prusament ASA @MMU2 0.8 nozzle] inherits = Prusament ASA @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material slowdown_below_layer_time = 20 filament_max_volumetric_speed = 14 [filament:Prusament PC Blend @MMU2 0.8 nozzle] inherits = Prusament PC Blend @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 12 [filament:Generic PETG @MMU2 0.8 nozzle] inherits = Generic PETG @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 18 first_layer_temperature = 240 temperature = 240 @@ -5687,7 +8847,7 @@ filament_ramming_parameters = "120 140 5.51613 5.6129 5.70968 5.77419 5.77419 5. [filament:Prusament PETG @MMU2 0.8 nozzle] inherits = Prusament PETG @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 18 first_layer_temperature = 240 temperature = 240 @@ -5704,26 +8864,26 @@ filament_colour = #BBBBBB [filament:Generic PLA @MMU2 0.8 nozzle] inherits = Generic PLA @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 14 first_layer_temperature = 215 temperature = 210 [filament:Prusament PLA @MMU2 0.8 nozzle] inherits = Prusament PLA @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 14 first_layer_temperature = 215 temperature = 210 [filament:Verbatim BVOH @MMU2 0.8 nozzle] inherits = Verbatim BVOH @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 8 [filament:PrimaSelect PVA+ @MMU2 0.8 nozzle] inherits = PrimaSelect PVA+ @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 8 ## Filaments 0.8 nozzle MINI @@ -9765,7 +12925,7 @@ inherits = Original Prusa i3 MK2S 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 @@ -9774,7 +12934,7 @@ inherits = Original Prusa i3 MK2S 0.25 nozzle 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 @@ -9784,7 +12944,7 @@ printer_model = MK2.5 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 @@ -9801,7 +12961,7 @@ deretract_speed = 20 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 @@ -9816,7 +12976,7 @@ max_print_height = 200 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.12.1 ; 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\n; select extruder\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; load to nozzle\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.20 F1000\nG1 X5 E4 F1000\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\n; select extruder\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; load to nozzle\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.20 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 [printer:Original Prusa i3 MK2.5 MMU2 Single 0.8 nozzle] @@ -9840,7 +13000,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK2.5S] @@ -9867,7 +13027,7 @@ max_print_height = 200 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle] @@ -9881,7 +13041,7 @@ retract_length = 0.7 retract_speed = 35 deretract_speed = 20 retract_lift = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 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 @@ -9906,7 +13066,7 @@ nozzle_diameter = 0.25 printer_variant = 0.25 retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK2.5S MMU2S] @@ -9918,7 +13078,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in 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.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK2.5S MMU2S 0.6 nozzle] @@ -9971,7 +13131,7 @@ color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change ## printer_variant = 0.8 ## retract_length = 1 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n ## [printer:Original Prusa i3 MK2.5 MMU2 0.8 nozzle] ## inherits = Original Prusa i3 MK2.5 MMU2 @@ -9981,7 +13141,7 @@ color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change ## printer_variant = 0.8 ## retract_length = 1 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n # XXXXXXXXXXXXXXXXX # XXX--- MK3 ---XXX @@ -10011,7 +13171,7 @@ remaining_times = 1 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_MK3\n retract_lift_below = 209 max_print_height = 210 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif}\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} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif}\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} printer_model = MK3 default_print_profile = 0.15mm QUALITY @MK3 thumbnails = 160x120 @@ -10023,7 +13183,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 E8 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 E8 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change @@ -10034,7 +13194,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 deretract_speed = 25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\nM221 S{if layer_height<0.075}100{else}95{endif} default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -10048,7 +13208,7 @@ retract_length = 0.7 retract_speed = 35 deretract_speed = 20 retract_lift = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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\nM221 S95 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\nM221 S95 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 @@ -10120,7 +13280,7 @@ default_filament_profile = Prusament PLA @MMU2 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.12.1 ; 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} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK3 MMU2 Single 0.6 nozzle] @@ -10131,7 +13291,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 deretract_speed = 25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -10146,7 +13306,7 @@ retract_length = 0.7 retract_speed = 35 deretract_speed = 20 retract_lift = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 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 @@ -10159,7 +13319,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 F1000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\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 value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 F1000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\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 value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change @@ -10168,7 +13328,7 @@ inherits = *mm2* 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.12.1 ; 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} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single] @@ -10176,7 +13336,7 @@ inherits = *mm2s* 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.12.1 ; 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} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.6 nozzle] @@ -10188,7 +13348,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 deretract_speed = 25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -10203,7 +13363,7 @@ retract_length = 0.7 retract_speed = 35 deretract_speed = 20 retract_lift = 0.25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 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 @@ -10217,7 +13377,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 F1400\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 value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 F1400\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 value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change @@ -10227,7 +13387,7 @@ renamed_from = "Original Prusa i3 MK3S MMU2S" 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.12.1 ; 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} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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; max_layer_z = [max_layer_z] ## 0.6mm nozzle MMU2/S printer profiles @@ -10240,7 +13400,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 deretract_speed = 25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -10251,7 +13411,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 deretract_speed = 25 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change @@ -10285,7 +13445,7 @@ color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change ## max_layer_height = 0.6 ## min_layer_height = 0.2 ## printer_variant = 0.8 -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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.0 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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.0 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle ## [printer:Original Prusa i3 MK3S & MK3S+ MMU2S 0.8 nozzle] @@ -10294,7 +13454,7 @@ color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change ## max_layer_height = 0.6 ## min_layer_height = 0.2 ## printer_variant = 0.8 -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.1 ; 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.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; 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.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle ## MINI @@ -10387,6 +13547,109 @@ retract_before_travel = 1.5 retract_speed = 45 deretract_speed = 20 +[printer:*commonXL*] +inherits = *common* +bed_shape = 0x0,360x0,360x360,0x360 +max_print_height = 360 +printer_variant = 0.4 +printer_model = XL +nozzle_diameter = 0.4 +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F720{endif} ; Move bed down\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X6 Y350 F6000 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+100, max_print_height)} F300{endif} ; Move bed down\nM900 K0 ; reset LA\nM142 S36 ; reset heatbreak target temp\nM221 S100 ; reset flow percentage\nM84 ; disable motors\n; max_layer_z = [max_layer_z] +machine_limits_usage = emit_to_gcode +machine_max_acceleration_e = 2500 +machine_max_acceleration_extruding = 3000 +machine_max_acceleration_retracting = 1200 +machine_max_acceleration_travel = 3000 +machine_max_acceleration_x = 5000 +machine_max_acceleration_y = 5000 +machine_max_acceleration_z = 200 +machine_max_feedrate_e = 100 +machine_max_feedrate_x = 400 +machine_max_feedrate_y = 400 +machine_max_feedrate_z = 20 +machine_max_jerk_e = 10 +machine_max_jerk_x = 8 +machine_max_jerk_y = 8 +machine_max_jerk_z = 2 +machine_min_extruding_rate = 0,0 +machine_min_travel_rate = 0,0 +max_layer_height = 0.25 +min_layer_height = 0.07 +silent_mode = 0 +remaining_times = 1 +printer_notes = PRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_XL\nPG +retract_lift_below = 359 +retract_speed = 35 +deretract_speed = 25 +retract_before_travel = 1.5 +retract_before_wipe = 80% +retract_layer_change = 1 +retract_length = 0.8 +start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\nM862.1 P[nozzle_diameter]\n; set & wait for bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_type[0] == "PC" or filament_type[0] == "NYLON") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; set extruder temp for bed leveling\nM109 T0 R{((filament_type[0] == "PC" or filament_type[0] == "NYLON") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; wait for temp\n; home carriage, pick tool, home all\nG28 XY\nM84 E ; turn off E motor\nG28 Z\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F4800\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\n{if first_layer_bed_temperature[0] > 60}\nG0 Z70 F480 ; move away (a bit more) in Z\nG0 X30 Y{print_bed_min[1]} F6000 ; move away in X/Y for higher bed temperatures\n{endif}\nM106 S100 ; cool off the nozzle\nM107 ; stop cooling off the nozzle - turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nM104 S[first_layer_temperature] ; set extruder temp\nG1 Z10 F720 ; move away in Z\nG0 X30 Y-8 F6000 ; move next to the sheet\n; wait for extruder temp\nM109 T0 S{first_layer_temperature[0]}\n;\n; purge\n;\nG92 E0 ; reset extruder position\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -8 : -5.5)} ; move close to the sheet's edge\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E10 X40 Z0.2 F500 ; purge\nG0 X70 E9 F800 ; purge\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG92 E0 ; reset extruder position\n +default_print_profile = 0.20mm QUALITY @XL 0.4 +default_filament_profile = "Prusament PLA @PG" +thumbnails = 16x16,313x173,440x240 +thumbnails_format = PNG +gcode_flavor = marlin2 +high_current_on_filament_swap = 0 +retract_lift = 0.3 + +[printer:Original Prusa XL 0.4 nozzle] +inherits = *commonXL* +max_layer_height = 0.30 + +[printer:Original Prusa XL 0.6 nozzle] +inherits = *commonXL* +printer_variant = 0.6 +nozzle_diameter = 0.6 +retract_length = 0.7 +retract_lift = 0.2 +max_layer_height = 0.40 +min_layer_height = 0.15 +default_print_profile = 0.25mm QUALITY @XL 0.6 +default_filament_profile = "Prusament PLA @PG 0.6" + +[printer:Original Prusa XL 0.5 nozzle] +inherits = *commonXL* +printer_variant = 0.5 +nozzle_diameter = 0.5 +retract_length = 0.7 +max_layer_height = 0.32 +min_layer_height = 0.07 +default_print_profile = 0.20mm QUALITY @XL 0.5 + +[printer:Original Prusa XL 0.3 nozzle] +inherits = *commonXL* +printer_variant = 0.3 +nozzle_diameter = 0.3 +retract_length = 0.7 +max_layer_height = 0.22 +min_layer_height = 0.05 +default_print_profile = 0.16mm QUALITY @XL 0.3 +machine_max_acceleration_travel = 2500 + +[printer:Original Prusa XL 0.25 nozzle] +inherits = *commonXL* +printer_variant = 0.25 +nozzle_diameter = 0.25 +retract_length = 0.8 +retract_lift = 0.15 +max_layer_height = 0.15 +min_layer_height = 0.05 +default_print_profile = 0.12mm QUALITY @XL 0.25 +machine_max_acceleration_travel = 1500 + +[printer:Original Prusa XL 0.8 nozzle] +inherits = *commonXL* +printer_variant = 0.8 +nozzle_diameter = 0.8 +retract_length = 0.8 +retract_lift = 0.25 +max_layer_height = 0.6 +min_layer_height = 0.2 +default_print_profile = 0.40mm QUALITY @XL 0.8 +default_filament_profile = "Prusament PLA @PG 0.8" + [printer:Original Prusa SL1] printer_technology = SLA printer_model = SL1 From 96cdcf72d3cbbc7bbcc7af5aae037265e326d34f Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 16 Mar 2023 09:43:19 +0100 Subject: [PATCH 087/201] Fix disable fan for first x layers not working correctly. Co-authored-by: Justin Schuh (@justinschuh) --- src/libslic3r/GCode/CoolingBuffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index b0f811ac4..b574c95b3 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -789,10 +789,11 @@ std::string CoolingBuffer::apply_layer_cooldown( } #undef EXTRUDER_CONFIG bridge_fan_control = bridge_fan_speed > fan_speed_new; - } else { + } else { // fan disabled bridge_fan_control = false; bridge_fan_speed = 0; fan_speed_new = 0; + custom_fan_speed_limits.second = 0; } if (fan_speed_new != m_fan_speed) { m_fan_speed = fan_speed_new; From 7a1440690257fc3960197f3bdc651a9aceef9369 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 16 Mar 2023 12:02:27 +0100 Subject: [PATCH 088/201] Missing overrides in PrusaConnect class. --- src/slic3r/Utils/OctoPrint.cpp | 12 ++++++++++++ src/slic3r/Utils/OctoPrint.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index de3ae943f..413a3445a 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -1135,4 +1135,16 @@ void PrusaConnect::set_http_post_header_args(Http& http, PrintHostPostUploadActi } +wxString PrusaConnect::get_test_ok_msg() const +{ + return _(L("Connection to PrusaConnect works correctly.")); +} + +wxString PrusaConnect::get_test_failed_msg(wxString& msg) const +{ + return GUI::from_u8((boost::format("%s: %s") + % _utf8(L("Could not connect to PrusaConnect")) + % std::string(msg.ToUTF8())).str()); +} + } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index d11cd9514..fd558eb2c 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -130,8 +130,11 @@ class PrusaConnect : public PrusaLink public: PrusaConnect(DynamicPrintConfig* config); ~PrusaConnect() override = default; + wxString get_test_ok_msg() const override; + wxString get_test_failed_msg(wxString& msg) const override; PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::QueuePrint; } const char* get_name() const override { return "PrusaConnect"; } + bool get_storage(wxArrayString& storage_path, wxArrayString& storage_name) const override { return false; } protected: void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override; }; From c06816212d03caf1eed89bb5c76f9f6a2a2e4704 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 8 Mar 2023 16:31:53 +0100 Subject: [PATCH 089/201] Duplicities in zip file. Show warning. Small refactoring. --- src/slic3r/GUI/FileArchiveDialog.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 36 +++++++++++++++++++++------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index fc2e27bf8..8d180c4fa 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -236,7 +236,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar } } // sorting files will help adjust_stack function to not create multiple same folders - std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); }); + std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() < p2.string(); }); size_t entry_count = 0; size_t depth = 1; for (const boost::filesystem::path& path : filtered_entries) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index defc8be3f..02c8959b9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5568,11 +5568,22 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) FileArchiveDialog dlg(static_cast(wxGetApp().mainframe), &archive, selected_paths); if (dlg.ShowModal() == wxID_OK) { + // selected_paths is already sorted + if (std::unique(selected_paths.begin(), selected_paths.end()) != selected_paths.end()) { + // notify about duplicities + wxString log = _L("Chosen paths to unzip contain duplicities. This will probably lead to fails during decompression."); + BOOST_LOG_TRIVIAL(warning) << log; + show_info(nullptr,log, _L("Warning")); + } std::string archive_path_string = archive_path.string(); archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4); - fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data()); - + std::vector> selected_paths_with_flag; // flag true if already loaded + size_t used_paths = 0; + selected_paths_with_flag.reserve(selected_paths.size()); + for (auto& path : selected_paths) { + selected_paths_with_flag.emplace_back(std::move(path), false); + } for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(&archive, i, &stat)) { wxString wname = boost::nowide::widen(stat.m_filename); @@ -5588,10 +5599,15 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) if (archive_path.empty()) continue; - for (const auto& path : selected_paths) { + for (auto& path_w_flag : selected_paths_with_flag) { + if (path_w_flag.second) + continue; + const fs::path& path = path_w_flag.first; if (path == archive_path) { try { + path_w_flag.second = true; + used_paths++; std::replace(name.begin(), name.end(), '\\', '/'); // rename if file exists std::string filename = path.filename().string(); @@ -5614,7 +5630,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive))); BOOST_LOG_TRIVIAL(error) << error_log; show_error(nullptr, error_log); - continue; + break; } fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc); file.write(buffer.c_str(), buffer.size()); @@ -5623,22 +5639,22 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string()); BOOST_LOG_TRIVIAL(error) << error_log; show_error(nullptr, error_log); - continue; + break; } BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path; - if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) { non_project_paths.emplace_back(final_path); - continue; + break; } // if 3mf - read archive headers to find project file if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) || (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) { non_project_paths.emplace_back(final_path); - continue; + break; } project_paths.emplace_back(final_path); + break; } catch (const std::exception& e) { @@ -5646,9 +5662,11 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) close_zip_reader(&archive); throw Slic3r::FileIOError(e.what()); } - break; + } } + if (used_paths == selected_paths_with_flag.size()) + break; } } close_zip_reader(&archive); From b8a479ea9a2c69486b0133cc2017471c6a9f1a0b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 9 Mar 2023 16:16:11 +0100 Subject: [PATCH 090/201] Get file size from Archive dialog, so correct file is opened. --- src/slic3r/GUI/FileArchiveDialog.cpp | 21 ++-- src/slic3r/GUI/FileArchiveDialog.hpp | 9 +- src/slic3r/GUI/Plater.cpp | 174 ++++++++++++--------------- 3 files changed, 98 insertions(+), 106 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 8d180c4fa..233a3e190 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -166,11 +166,11 @@ ArchiveViewCtrl::~ArchiveViewCtrl() } } -FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector& selected_paths) +FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector>& selected_paths_w_size) : DPIDialog(parent_window, wxID_ANY, _(L("Archive preview")), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) - , m_selected_paths (selected_paths) + , m_selected_paths_w_size (selected_paths_w_size) { #ifdef _WIN32 wxGetApp().UpdateDarkUI(this); @@ -213,7 +213,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase); mz_uint num_entries = mz_zip_reader_get_num_files(archive); mz_zip_archive_file_stat stat; - std::vector filtered_entries; + std::vector> filtered_entries; // second is unzipped size for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(archive, i, &stat)) { std::string extra(1024, 0); @@ -232,22 +232,25 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar // filter out MACOS specific hidden files if (boost::algorithm::starts_with(path.string(), "__MACOSX")) continue; - filtered_entries.emplace_back(std::move(path)); + filtered_entries.emplace_back(std::move(path), stat.m_uncomp_size); } } // sorting files will help adjust_stack function to not create multiple same folders - std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() < p2.string(); }); + std::sort(filtered_entries.begin(), filtered_entries.end(), [](const std::pair& p1, const std::pair& p2){ return p1.first.string() < p2.first.string(); }); size_t entry_count = 0; size_t depth = 1; - for (const boost::filesystem::path& path : filtered_entries) + for (const auto& entry : filtered_entries) { + const boost::filesystem::path& path = entry.first; std::shared_ptr parent(nullptr); depth = std::max(depth, adjust_stack(path, stack)); if (!stack.empty()) parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files - m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + std::shared_ptr new_node = m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()), false); + new_node->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + new_node->set_size(entry.second); entry_count++; } } @@ -305,12 +308,12 @@ void FileArchiveDialog::on_open_button() wxDataViewItemArray top_items; m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items); - std::function deep_fill = [&paths = m_selected_paths, &deep_fill](ArchiveViewNode* node){ + std::function deep_fill = [&paths = m_selected_paths_w_size, &deep_fill](ArchiveViewNode* node){ if (node == nullptr) return; if (node->get_children().empty()) { if (node->get_toggle()) - paths.emplace_back(node->get_fullpath()); + paths.emplace_back(node->get_fullpath(), node->get_size()); } else { for (std::shared_ptr child : node->get_children()) deep_fill(child.get()); diff --git a/src/slic3r/GUI/FileArchiveDialog.hpp b/src/slic3r/GUI/FileArchiveDialog.hpp index 22a9ecedf..4a335c75a 100644 --- a/src/slic3r/GUI/FileArchiveDialog.hpp +++ b/src/slic3r/GUI/FileArchiveDialog.hpp @@ -33,6 +33,8 @@ public: void set_is_folder(bool is_folder) { m_folder = is_folder; } void set_fullpath(boost::filesystem::path path) { m_fullpath = path; } boost::filesystem::path get_fullpath() const { return m_fullpath; } + void set_size(size_t size) { m_size = size; } + size_t get_size() const { return m_size; } private: wxString m_name; @@ -43,6 +45,7 @@ private: bool m_folder { false }; boost::filesystem::path m_fullpath; bool m_container { false }; + size_t m_size { 0 }; }; class ArchiveViewModel : public wxDataViewModel @@ -100,7 +103,7 @@ protected: class FileArchiveDialog : public DPIDialog { public: - FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector& selected_paths); + FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector>& selected_paths_w_size); protected: void on_dpi_changed(const wxRect& suggested_rect) override; @@ -109,7 +112,9 @@ protected: void on_all_button(); void on_none_button(); - std::vector& m_selected_paths; + // chosen files are written into this vector and returned to caller via reference. + // path in archive and decompressed size. The size can be used to distinguish between files with same path. + std::vector>& m_selected_paths_w_size; ArchiveViewCtrl* m_avc; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 02c8959b9..e89dd2624 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5558,115 +5558,99 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) std::string err_msg = GUI::format(_utf8("Loading of a zip archive on path %1% has failed."), archive_path.string()); throw Slic3r::FileIOError(err_msg); } - mz_uint num_entries = mz_zip_reader_get_num_files(&archive); - mz_zip_archive_file_stat stat; - - std::vector selected_paths; - + // selected_paths contains paths and its uncompressed size. The size is used to distinguish between files with same path. + std::vector> selected_paths; FileArchiveDialog dlg(static_cast(wxGetApp().mainframe), &archive, selected_paths); if (dlg.ShowModal() == wxID_OK) - { - // selected_paths is already sorted - if (std::unique(selected_paths.begin(), selected_paths.end()) != selected_paths.end()) { - // notify about duplicities - wxString log = _L("Chosen paths to unzip contain duplicities. This will probably lead to fails during decompression."); - BOOST_LOG_TRIVIAL(warning) << log; - show_info(nullptr,log, _L("Warning")); - } + { std::string archive_path_string = archive_path.string(); archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4); fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data()); - std::vector> selected_paths_with_flag; // flag true if already loaded - size_t used_paths = 0; - selected_paths_with_flag.reserve(selected_paths.size()); - for (auto& path : selected_paths) { - selected_paths_with_flag.emplace_back(std::move(path), false); - } - for (mz_uint i = 0; i < num_entries; ++i) { - if (mz_zip_reader_file_stat(&archive, i, &stat)) { - wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = boost::nowide::narrow(wname); - fs::path archive_path(name); - - std::string extra(1024, 0); - size_t extra_size = mz_zip_reader_get_filename_from_extra(&archive, i, extra.data(), extra.size()); - if (extra_size > 0) { - archive_path = fs::path(extra.substr(0, extra_size)); - name = archive_path.string(); - } - - if (archive_path.empty()) - continue; - for (auto& path_w_flag : selected_paths_with_flag) { - if (path_w_flag.second) + + for (auto& path_w_size : selected_paths) { + const fs::path& path = path_w_size.first; + size_t size = path_w_size.second; + // find path in zip archive + for (mz_uint i = 0; i < num_entries; ++i) { + if (mz_zip_reader_file_stat(&archive, i, &stat)) { + if (size != stat.m_uncomp_size) // size must fit continue; - const fs::path& path = path_w_flag.first; - if (path == archive_path) { - try + wxString wname = boost::nowide::widen(stat.m_filename); + std::string name = boost::nowide::narrow(wname); + fs::path archive_path(name); + + std::string extra(1024, 0); + size_t extra_size = mz_zip_reader_get_filename_from_extra(&archive, i, extra.data(), extra.size()); + if (extra_size > 0) { + archive_path = fs::path(extra.substr(0, extra_size)); + name = archive_path.string(); + } + + if (archive_path.empty()) + continue; + if (path != archive_path) + continue; + // decompressing + try + { + std::replace(name.begin(), name.end(), '\\', '/'); + // rename if file exists + std::string filename = path.filename().string(); + std::string extension = boost::filesystem::extension(path); + std::string just_filename = filename.substr(0, filename.size() - extension.size()); + std::string final_filename = just_filename; + + size_t version = 0; + while (fs::exists(archive_dir / (final_filename + extension))) { - path_w_flag.second = true; - used_paths++; - std::replace(name.begin(), name.end(), '\\', '/'); - // rename if file exists - std::string filename = path.filename().string(); - std::string extension = boost::filesystem::extension(path); - std::string just_filename = filename.substr(0, filename.size() - extension.size()); - std::string final_filename = just_filename; - - size_t version = 0; - while (fs::exists(archive_dir / (final_filename + extension))) - { - ++version; - final_filename = just_filename + "(" + std::to_string(version) + ")"; - } - filename = final_filename + extension; - fs::path final_path = archive_dir / filename; - - std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); - if (res == 0) { - wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive))); - BOOST_LOG_TRIVIAL(error) << error_log; - show_error(nullptr, error_log); - break; - } - fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc); - file.write(buffer.c_str(), buffer.size()); - file.close(); - if (!fs::exists(final_path)) { - wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string()); - BOOST_LOG_TRIVIAL(error) << error_log; - show_error(nullptr, error_log); - break; - } - BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path; - if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) { - non_project_paths.emplace_back(final_path); - break; - } - // if 3mf - read archive headers to find project file - if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) || - (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) { - non_project_paths.emplace_back(final_path); - break; - } - - project_paths.emplace_back(final_path); + ++version; + final_filename = just_filename + "(" + std::to_string(version) + ")"; + } + filename = final_filename + extension; + fs::path final_path = archive_dir / filename; + std::string buffer((size_t)stat.m_uncomp_size, 0); + // Decompress action. We already has correct file index in stat structure. + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) { + wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive))); + BOOST_LOG_TRIVIAL(error) << error_log; + show_error(nullptr, error_log); break; } - catch (const std::exception& e) - { - // ensure the zip archive is closed and rethrow the exception - close_zip_reader(&archive); - throw Slic3r::FileIOError(e.what()); + // write buffer to file + fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc); + file.write(buffer.c_str(), buffer.size()); + file.close(); + if (!fs::exists(final_path)) { + wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string()); + BOOST_LOG_TRIVIAL(error) << error_log; + show_error(nullptr, error_log); + break; } - + BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path; + if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) { + non_project_paths.emplace_back(final_path); + break; + } + // if 3mf - read archive headers to find project file + if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) || + (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) { + non_project_paths.emplace_back(final_path); + break; + } + + project_paths.emplace_back(final_path); + break; + } + catch (const std::exception& e) + { + // ensure the zip archive is closed and rethrow the exception + close_zip_reader(&archive); + throw Slic3r::FileIOError(e.what()); } } - if (used_paths == selected_paths_with_flag.size()) - break; } } close_zip_reader(&archive); From 8ed8adfd3d6f4fd98da69ba27442797049f56b34 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 16 Mar 2023 14:25:36 +0100 Subject: [PATCH 091/201] Fix anchoring over sparse infill bug causing overextrusion when stacking thick bridges onto each other --- src/libslic3r/PrintObject.cpp | 90 ++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5e07ebc69..b72a2bacb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1627,7 +1627,7 @@ void PrintObject::bridge_over_infill() contains_only_lightning = false; } Polygons fill_polys = to_polygons(region->fill_expolygons()); - unsupported_area = union_(unsupported_area, expand(fill_polys, spacing)); + unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end()); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); @@ -1635,9 +1635,10 @@ void PrintObject::bridge_over_infill() } } } + unsupported_area = closing(unsupported_area, SCALED_EPSILON); lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - unsupported_area = shrink(unsupported_area, 5 * spacing); + unsupported_area = shrink(unsupported_area, 4 * spacing); unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { @@ -1647,18 +1648,25 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - for (Polygon p : diff(to_polygons(s->expolygon), worth_bridging)) { - if (p.area() < region->flow(frSolidInfill, true).scaled_spacing() * scale_(12.0)) { + for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) { + auto area = p.area(); + if (area < spacing * scale_(12.0) && area > spacing * spacing) { worth_bridging.push_back(p); } } - worth_bridging = intersection(closing(worth_bridging, 3 * region->flow(frSolidInfill, true).scaled_spacing()), s->expolygon); + worth_bridging = intersection(closing(worth_bridging, 2 * spacing), s->expolygon); candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)), to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging), to_lines(unsupported_area)); +#endif +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(s->expolygon)), + to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), + to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))), + to_lines(unsupported_area)); #endif } } @@ -1765,9 +1773,9 @@ void PrintObject::bridge_over_infill() // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. auto gather_areas_w_depth = [target_flow_height_factor](const PrintObject *po, int lidx, float target_flow_height) { - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons not_sparse_infill{}; + // Gather layers sparse infill areas, to depth defined by used bridge flow + ExPolygons layers_sparse_infill{}; + ExPolygons not_sparse_infill{}; double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; for (int i = int(lidx) - 1; i >= 0; --i) { // Stop iterating if layer is lower than bottom_z. @@ -1778,19 +1786,19 @@ void PrintObject::bridge_over_infill() for (const LayerRegion *region : layer->regions()) { bool has_low_density = region->region().config().fill_density.value < 100; for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density) { - Polygons p = to_polygons(surface.expolygon); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); + if ((surface.surface_type == stInternal && has_low_density) || surface.surface_type == stInternalVoid ) { + layers_sparse_infill.push_back(surface.expolygon); } else { - Polygons p = to_polygons(surface.expolygon); - not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); + not_sparse_infill.push_back(surface.expolygon); } } } - lower_layers_sparse_infill = union_(lower_layers_sparse_infill); } - lower_layers_sparse_infill = closing(lower_layers_sparse_infill, SCALED_EPSILON); - return diff(lower_layers_sparse_infill, not_sparse_infill); + layers_sparse_infill = union_ex(layers_sparse_infill); + layers_sparse_infill = closing_ex(layers_sparse_infill, SCALED_EPSILON); + not_sparse_infill = union_ex(not_sparse_infill); + not_sparse_infill = closing_ex(not_sparse_infill, SCALED_EPSILON); + return diff(layers_sparse_infill, not_sparse_infill); }; // LAMBDA do determine optimal bridging angle @@ -1927,7 +1935,7 @@ void PrintObject::bridge_over_infill() }); if (maybe_below_anchor != anchors_intersections.rend()) { section.a = maybe_below_anchor->first; - section.a.y() -= bridging_flow.scaled_width() * (0.5 + 1.0); + section.a.y() -= bridging_flow.scaled_width() * (0.5 + 0.5); } auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b, @@ -1936,7 +1944,7 @@ void PrintObject::bridge_over_infill() }); if (maybe_upper_anchor != anchors_intersections.end()) { section.b = maybe_upper_anchor->first; - section.b.y() += bridging_flow.scaled_width() * (0.5 + 1.0); + section.b.y() += bridging_flow.scaled_width() * (0.5 + 0.5); } } @@ -2059,6 +2067,7 @@ void PrintObject::bridge_over_infill() }); // Gather deep infill areas, where thick bridges fit + coordf_t spacing = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).scaled_spacing(); coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor; Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height); @@ -2079,6 +2088,8 @@ void PrintObject::bridge_over_infill() } } + deep_infill_area = expand(deep_infill_area, spacing); + // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors Polygons expansion_area; Polygons total_fill_area; @@ -2088,30 +2099,42 @@ void PrintObject::bridge_over_infill() Polygons fill_polys = to_polygons(region->fill_expolygons()); total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end()); } - total_fill_area = closing(total_fill_area, SCALED_EPSILON); - expansion_area = closing(expansion_area, SCALED_EPSILON); - expansion_area = intersection(expansion_area, deep_infill_area); - Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); + total_fill_area = closing(total_fill_area, SCALED_EPSILON); + expansion_area = closing(expansion_area, SCALED_EPSILON); + expansion_area = intersection(expansion_area, deep_infill_area); + Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); + +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + + "_total_area", + to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors)); +#endif + std::vector expanded_surfaces; expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { - const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); - Polygons area_to_be_bridge = intersection(candidate.new_polys, deep_infill_area); + const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); + Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); + area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); + Polygons limiting_area = union_(area_to_be_bridge, expansion_area); if (area_to_be_bridge.empty()) continue; - area_to_be_bridge = expand(area_to_be_bridge, flow.scaled_spacing()); - Polylines boundary_plines = to_polylines(expand(total_fill_area, 1.3 * flow.scaled_spacing())); { - Polylines expansion_plines = to_polylines(expansion_area); - boundary_plines.insert(boundary_plines.end(), expansion_plines.begin(), expansion_plines.end()); - Polylines expanded_expansion_plines = to_polylines(expand(expansion_area, 1.3 * flow.scaled_spacing())); - boundary_plines.insert(boundary_plines.end(), expanded_expansion_plines.begin(), expanded_expansion_plines.end()); + Polylines limiting_plines = to_polylines(expand(limiting_area, 0.3*flow.spacing())); + boundary_plines.insert(boundary_plines.end(), limiting_plines.begin(), limiting_plines.end()); } +#ifdef DEBUG_BRIDGE_OVER_INFILL + int r = rand(); + debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + + "_anchors_" + std::to_string(r), + to_lines(area_to_be_bridge), to_lines(boundary_plines), to_lines(anchors), to_lines(expansion_area)); +#endif + double bridging_angle = 0; if (!anchors.empty()) { bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), @@ -2145,14 +2168,13 @@ void PrintObject::bridge_over_infill() } bridging_area = opening(bridging_area, flow.scaled_spacing()); + bridging_area = intersection(bridging_area, limiting_area); bridging_area = intersection(bridging_area, total_fill_area); expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + - "_expanded_bridging", - to_lines(layer->lslices), to_lines(boundary_plines), to_lines(candidate.new_polys), - to_lines(bridging_area)); + debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_expanded_bridging" + std::to_string(r), + to_lines(layer->lslices), to_lines(boundary_plines), to_lines(candidate.new_polys), to_lines(bridging_area)); #endif expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area, From c878bbd8264e6eb86a15acb1775c371964522c1b Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:44:51 +0100 Subject: [PATCH 092/201] Updated compatible conditions for some filament profiles. --- resources/profiles/PrusaResearch.idx | 2 ++ resources/profiles/PrusaResearch.ini | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 97d18c1be..05970389c 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.0-alpha5 +1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. min_slic3r_version = 2.6.0-alpha1 1.7.0-alpha0 Added profiles for Print With Smile filaments. @@ -6,6 +7,7 @@ min_slic3r_version = 2.6.0-alpha1 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. min_slic3r_version = 2.5.1-rc0 +1.6.2 Updated compatibility condition in some filament profiles (Prusa XL). 1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles. 1.6.0 Added Original Prusa XL profiles. Updated acceleration settings for Prusa MINI. Updated infill/perimeter overlap values. min_slic3r_version = 2.5.0-alpha0 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 1776d5e95..3fb3aee3d 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.7.0-alpha1 +config_version = 1.7.0-alpha2 # 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% @@ -5178,7 +5178,7 @@ compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG* first_layer_bed_temperature = 100 -[filament:Filatech FilaCarbonc 0.6] +[filament:Filatech FilaCarbon @PG 0.6] inherits = Filatech FilaCarbon @PG; *ABS06PG* [filament:Filatech FilaCarbon @PG 0.8] @@ -6428,7 +6428,7 @@ extrusion_multiplier = 1.03 filament_cost = 54.99 filament_density = 1.27 filament_colour = #BBBBBB -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:Prusament PETG Carbon Fiber @PG] inherits = Prusament PETG; *PETPG*; *04PLUSPG* @@ -7969,7 +7969,7 @@ temperature = 285 first_layer_bed_temperature = 90 bed_temperature = 90 fan_below_layer_time = 10 -compatible_printers_condition = printer_model!="MINI" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material max_fan_speed = 15 min_fan_speed = 15 filament_type = PA From 19d02e6d749b844c2bcd5b8c158f923720380b99 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Mar 2023 09:29:28 +0100 Subject: [PATCH 093/201] Fix: ../src/slic3r/GUI/IconManager.cpp:174:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] ../src/slic3r/GUI/SurfaceDrag.cpp:104:25: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector::size_type' {aka 'long unsigned int'} [-Wsign-compare] ../src/slic3r/GUI/SurfaceDrag.cpp:57:30: warning: 'std::optional Slic3r::GUI::calc_scale(const Matrix3d&, const Matrix3d&, const Vec3d&)' defined but not used [-Wunused-function] ../src/slic3r/Utils/RaycastManager.cpp:14:56: warning: suggest parentheses around '&&' within '||' [-Wparentheses] ../src/slic3r/Utils/RaycastManager.cpp:316:32: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector::size_type' {aka 'long unsigned int'} [-Wsign-compare] --- src/slic3r/GUI/IconManager.cpp | 2 +- src/slic3r/GUI/SurfaceDrag.cpp | 16 +++++++++------- src/slic3r/Utils/RaycastManager.cpp | 9 ++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 1974e917c..45c76887c 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -171,7 +171,7 @@ void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_ return; } - ImTextureID id = (void *) icon.tex_id; + ImTextureID id = (void *)static_cast(icon.tex_id); const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); } diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index f32113698..4a48ced29 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -53,8 +53,8 @@ static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const return nearest_offset; } - // Calculate scale in world -static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) + // Calculate scale in world for check in debug +[[maybe_unused]] static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) { Vec3d from_dir = from * dir; Vec3d to_dir = to * dir; @@ -98,11 +98,13 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, return false; // is selected volume closest hovered? - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - int hovered_idx = canvas.get_first_hover_volume_idx(); - if (hovered_idx < 0 || - hovered_idx >= gl_volumes.size() || - gl_volumes[hovered_idx] != gl_volume) + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + if (int hovered_idx = canvas.get_first_hover_volume_idx(); + hovered_idx < 0) + return false; + else if (auto hovered_idx_ = static_cast(hovered_idx); + hovered_idx_ >= gl_volumes.size() || + gl_volumes[hovered_idx_] != gl_volume) return false; const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 63cb580db..80a716755 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -11,7 +11,7 @@ static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelIn return std::make_pair(instance.id().id, volume.id().id); } static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { - return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } + return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); } static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; } @@ -313,9 +313,12 @@ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::A RaycastManager::Meshes meshes; for (const std::shared_ptr &caster : casters) { int index = SceneRaycaster::decode_id(type, caster->get_id()); - if (index < 0 || index >= gl_volumes.size()) + if (index < 0) continue; - const GLVolume *gl_volume = gl_volumes[index]; + auto index_ = static_cast(index); + if(index_ >= gl_volumes.size()) + continue; + const GLVolume *gl_volume = gl_volumes[index_]; const ModelVolume *volume = get_model_volume(*gl_volume, objects); size_t id = volume->id().id; if (condition.skip(id)) From d5f229a249efd1c30378d1fd670dbb19bdf7dafe Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 9 Mar 2023 17:14:36 +0100 Subject: [PATCH 094/201] Refactoring of miniz extract calls. Use file_index instead of filename as identifier. --- src/libslic3r/Format/3mf.cpp | 18 +++++++++--------- src/libslic3r/Format/AMF.cpp | 2 +- src/libslic3r/Format/ZipperArchiveImport.cpp | 4 ++-- src/slic3r/Utils/PresetUpdater.cpp | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 260e82e81..34594240f 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -957,7 +957,7 @@ namespace Slic3r { try { - res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { + res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { CallbackData* data = (CallbackData*)pOpaque; if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.parse_error()) { char error_buf[1024]; @@ -991,7 +991,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading cut information data to buffer"); return; @@ -1053,7 +1053,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading config data to buffer"); return; @@ -1073,7 +1073,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading layer heights profile data to buffer"); return; @@ -1135,7 +1135,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading layer config ranges data to buffer"); return; @@ -1192,7 +1192,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading sla support points data to buffer"); return; @@ -1274,7 +1274,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer(size_t(stat.m_uncomp_size), 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading sla support points data to buffer"); return; @@ -1379,7 +1379,7 @@ namespace Slic3r { return false; } - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, parser_buffer, (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading config data to buffer"); return false; @@ -1399,7 +1399,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading custom Gcodes per height data to buffer"); return; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 7acec1771..a38960324 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -965,7 +965,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi try { - res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { + res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { CallbackData* data = (CallbackData*)pOpaque; if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->ctx.error()) { diff --git a/src/libslic3r/Format/ZipperArchiveImport.cpp b/src/libslic3r/Format/ZipperArchiveImport.cpp index 5b0ecff6e..2bd5f555b 100644 --- a/src/libslic3r/Format/ZipperArchiveImport.cpp +++ b/src/libslic3r/Format/ZipperArchiveImport.cpp @@ -18,7 +18,7 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, { std::string buf(size_t(entry.m_uncomp_size), '\0'); - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + if (!mz_zip_reader_extract_to_mem(&zip.arch, entry.m_file_index, buf.data(), buf.size(), 0)) throw Slic3r::FileIOError(zip.get_errorstr()); @@ -35,7 +35,7 @@ EntryBuffer read_entry(const mz_zip_archive_file_stat &entry, { std::vector buf(entry.m_uncomp_size); - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + if (!mz_zip_reader_extract_to_mem(&zip.arch, entry.m_file_index, buf.data(), buf.size(), 0)) throw Slic3r::FileIOError(zip.get_errorstr()); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index e7bea4819..028c7ce0a 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -399,7 +399,7 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string std::string name(stat.m_filename); if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { BOOST_LOG_TRIVIAL(error) << "Failed to unzip " << stat.m_filename; continue; From b61a6f293d0dd638e17acafeb86e67d037e46c92 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Mar 2023 12:56:25 +0100 Subject: [PATCH 095/201] fix problem with missing lslice Links - detection could occasionally fail, issue 9744 --- src/libslic3r/Layer.cpp | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 7962f0376..2d1132283 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -1,11 +1,14 @@ #include "Layer.hpp" #include "ClipperZUtils.hpp" #include "ClipperUtils.hpp" +#include "Point.hpp" +#include "Polygon.hpp" #include "Print.hpp" #include "Fill/Fill.hpp" #include "ShortestPath.hpp" #include "SVG.hpp" #include "BoundingBox.hpp" +#include "clipper/clipper.hpp" #include @@ -180,6 +183,31 @@ static void connect_layer_slices( break; } } + if (!found) { + // The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection. + // The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size! + // example of failing link on two layers, each with single polygon without holes. + // layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)} + // layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)} + // note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()). + // that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely" + Polygon contour_poly; + for (const auto& p : polynode.Contour) { + contour_poly.points.emplace_back(p.x(), p.y()); + } + BoundingBox contour_aabb{contour_poly.points}; + for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; --l) { + LayerSlice &lslice = m_above.lslices_ex[l]; + // it is potentially slow, but should be executed rarely + if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_above.lslices[l]).empty()) { + found = true; + j = l; + assert(i >= 0 && i < m_below.lslices_ex.size()); + assert(j >= 0 && j < m_above.lslices_ex.size()); + break; + } + } + } } else { // Index of an island above. Look-it up in the island below. assert(j < m_offset_end); @@ -194,6 +222,23 @@ static void connect_layer_slices( break; } } + if (!found) { // Explanation for aditional check is above. + Polygon contour_poly; + for (const auto &p : polynode.Contour) { + contour_poly.points.emplace_back(p.x(), p.y()); + } + BoundingBox contour_aabb{contour_poly.points}; + for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; --l) { + LayerSlice &lslice = m_below.lslices_ex[l]; + if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_below.lslices[l]).empty()) { + found = true; + i = l; + assert(i >= 0 && i < m_below.lslices_ex.size()); + assert(j >= 0 && j < m_above.lslices_ex.size()); + break; + } + } + } } } else { assert(assert_intersection_valid(i, j)); From 520148a8aaf163c30331ddd6d46872797b98b60d Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:48:06 +0100 Subject: [PATCH 096/201] Added new AnkerMake resources --- resources/profiles/Anker/M5-texture_v2.svg | 1 + resources/profiles/Anker/M5_thumbnail_v2.png | Bin 0 -> 26339 bytes 2 files changed, 1 insertion(+) create mode 100644 resources/profiles/Anker/M5-texture_v2.svg create mode 100644 resources/profiles/Anker/M5_thumbnail_v2.png diff --git a/resources/profiles/Anker/M5-texture_v2.svg b/resources/profiles/Anker/M5-texture_v2.svg new file mode 100644 index 000000000..4fcf959f6 --- /dev/null +++ b/resources/profiles/Anker/M5-texture_v2.svg @@ -0,0 +1 @@ + diff --git a/resources/profiles/Anker/M5_thumbnail_v2.png b/resources/profiles/Anker/M5_thumbnail_v2.png new file mode 100644 index 0000000000000000000000000000000000000000..a03a7bc08d9de13d5f6699f5e846db293180bdb4 GIT binary patch literal 26339 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 literal 0 HcmV?d00001 From d4d463bc289a129d679ac0e2a7b47cd1bb894b62 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:48:36 +0100 Subject: [PATCH 097/201] Official AnkerMake profiles. --- resources/profiles/Anker.idx | 6 +- resources/profiles/Anker.ini | 189 +++++++++++++++-------------------- 2 files changed, 84 insertions(+), 111 deletions(-) diff --git a/resources/profiles/Anker.idx b/resources/profiles/Anker.idx index 49e15b3d0..f96cee247 100644 --- a/resources/profiles/Anker.idx +++ b/resources/profiles/Anker.idx @@ -1,3 +1,3 @@ -min_slic3r_version = 2.6.0-alpha1 -1.0.1 Disabled thick bridges. -1.0.0 Initial Version +min_slic3r_version = 2.6.0-alpha4 +1.1.1 Initial official version +1.0.1 Initial Version diff --git a/resources/profiles/Anker.ini b/resources/profiles/Anker.ini index 34592e1e5..70cb4dcf1 100644 --- a/resources/profiles/Anker.ini +++ b/resources/profiles/Anker.ini @@ -1,14 +1,14 @@ # Print profiles for the AnkerMake printers. -# https://github.com/prusa3d/PrusaSlicer/pull/9075 by @just-trey [vendor] # Vendor name will be shown by the Config Wizard. -name = Anker +name = AnkerMake # 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.1.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anker/ +# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # 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. @@ -20,8 +20,9 @@ variants = 0.4 technology = FFF family = AnkerMake bed_model = M5-bed.stl -bed_texture = M5-texture.svg -default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER +bed_texture = M5-texture_v2.svg +thumbnail = M5_thumbnail_v2.png +default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER; # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -31,8 +32,8 @@ default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; avoid_crossing_perimeters = 0 bridge_acceleration = 2500 bridge_angle = 0 -bridge_flow_ratio = 0.95 -bridge_speed = 150 +bridge_flow_ratio = 1 +bridge_speed = 50 brim_separation = 0.1 brim_type = outer_only brim_width = 0 @@ -40,32 +41,36 @@ clip_multipart_objects = 1 complete_objects = 0 default_acceleration = 2500 dont_support_bridges = 1 -elefant_foot_compensation = 0.1 +elefant_foot_compensation = 0.2 ensure_vertical_shell_thickness = 1 external_perimeter_speed = 150 external_perimeters_first = 0 extra_perimeters = 0 extruder_clearance_height = 30 extruder_clearance_radius = 45 +extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.44 + fill_angle = 45 -fill_density = 15% -fill_pattern = cubic +fill_density = 10% +fill_pattern = grid first_layer_acceleration = 2500 first_layer_acceleration_over_raft = 0 -first_layer_extrusion_width = 200% +first_layer_extrusion_width = 0.4 first_layer_speed = 50% first_layer_speed_over_raft = 30 gap_fill_enabled = 1 gap_fill_speed = 150 gcode_comments = 0 infill_acceleration = 2500 -infill_anchor = 600% -infill_anchor_max = 50 +infill_anchor = 2.5 +infill_anchor_max = 12 infill_every_layers = 1 infill_extruder = 1 infill_first = 0 +infill_extrusion_width = 0.4 infill_only_where_needed = 0 -infill_overlap = 23% +infill_overlap = 10% infill_speed = 250 interface_shells = 0 max_print_speed = 250 @@ -76,18 +81,18 @@ min_skirt_length = 4 notes = only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{digits(layer_height,1,2)}mm_{filament_type[0]}_{printer_model}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}.gcode overhangs = 1 perimeter_acceleration = 2500 perimeter_extruder = 1 -perimeter_extrusion_width = 0 -perimeter_generator = arachne +perimeter_extrusion_width = 0.4 +perimeter_generator = classic perimeter_speed = 250 perimeters = 3 post_process = print_settings_id = raft_layers = 0 -resolution = 0 +resolution = 0.01 seam_position = aligned single_extruder_multi_material_priming = 0 skirt_distance = 3 @@ -97,32 +102,37 @@ small_perimeter_speed = 150 solid_infill_below_area = 0 solid_infill_every_layers = 0 solid_infill_extruder = 1 -solid_infill_speed = 175 +solid_infill_extrusion_width = 0.4 +solid_infill_speed = 250 spiral_vase = 0 standby_temperature_delta = -5 +support_material_auto = 0 support_material = 0 support_material_angle = 0 support_material_buildplate_only = 0 -support_material_contact_distance = 0.15 +support_material_contact_distance = 0.1 support_material_enforce_layers = 0 support_material_extruder = 0 support_material_interface_contact_loops = 0 support_material_interface_extruder = 0 support_material_interface_layers = 2 support_material_interface_spacing = 0.2 -support_material_interface_speed = 100% +support_material_interface_speed = 80% support_material_pattern = rectilinear support_material_spacing = 2 -support_material_speed = 125 +support_material_speed = 150 support_material_synchronize_layers = 0 -support_material_threshold = 40 +support_material_threshold = 55 support_material_with_sheath = 0 -support_material_xy_spacing = 60% +support_material_xy_spacing = 50% thick_bridges = 0 thin_walls = 0 top_solid_infill_speed = 150 -travel_speed = 300 -travel_speed_z = 10 +top_infill_extrusion_width = 0.4 +top_fill_pattern = rectilinear +bottom_fill_pattern = rectilinear +travel_speed = 250 +travel_speed_z = 0 wipe_tower = 0 wipe_tower_bridging = 10 wipe_tower_rotation_angle = 0 @@ -131,86 +141,49 @@ wipe_tower_x = 170 wipe_tower_y = 140 xy_size_compensation = 0 -[print:*0.08mm*] -inherits = *common* -layer_height = 0.08 -first_layer_height = 0.12 -bottom_solid_layers = 9 -top_solid_layers = 11 -bridge_flow_ratio = 0.70 - [print:*0.10mm*] inherits = *common* layer_height = 0.10 -first_layer_height = 0.14 +first_layer_height = 0.10 bottom_solid_layers = 7 top_solid_layers = 9 -bridge_flow_ratio = 0.70 - -[print:*0.12mm*] -inherits = *common* -layer_height = 0.12 -first_layer_height = 0.16 -bottom_solid_layers = 6 -top_solid_layers = 7 -bridge_flow_ratio = 0.70 - -[print:*0.16mm*] -inherits = *common* -layer_height = 0.16 -first_layer_height = 0.20 -bottom_solid_layers = 5 -top_solid_layers = 7 -bridge_flow_ratio = 0.85 +bridge_flow_ratio = 1 [print:*0.20mm*] inherits = *common* layer_height = 0.20 -first_layer_height = 0.24 +first_layer_height = 0.14 bottom_solid_layers = 4 top_solid_layers = 5 -[print:*0.24mm*] +[print:*0.30mm*] inherits = *common* -layer_height = 0.24 -first_layer_height = 0.28 +layer_height = 0.30 +first_layer_height = 0.21 bottom_solid_layers = 3 top_solid_layers = 4 -[print:*0.28mm*] -inherits = *common* -layer_height = 0.28 -first_layer_height = 0.28 -bottom_solid_layers = 3 -top_solid_layers = 4 - -[print:0.08 mm SUPERDETAIL (0.4 mm nozzle) @ANKER] -inherits = *0.08mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 [print:0.10 mm HIGHDETAIL (0.4 mm nozzle) @ANKER] inherits = *0.10mm* compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 -[print:0.12 mm DETAIL (0.4 mm nozzle) @ANKER] -inherits = *0.12mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 - -[print:0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER] -inherits = *0.16mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 - [print:0.20 mm NORMAL (0.4 mm nozzle) @ANKER] inherits = *0.20mm* compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 -[print:0.24 mm DRAFT (0.4 mm nozzle) @ANKER] -inherits = *0.24mm* + +[print:0.30 mm SUPERDRAFT (0.4 mm nozzle) @ANKER] +inherits = *0.30mm* compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 -[print:0.28 mm SUPERDRAFT (0.4 mm nozzle) @ANKER] -inherits = *0.28mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 +# 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. +# Also print the following bed adhesion test at 0.1 layer height as well: +# https://www.prusaprinters.org/prints/4634-bed-adhesion-warp-test +# At least for PLA, please keep bed temp at 60, as many Creality printers do not have any ABL +# So having some leeway to get good bed adhesion is not a luxury for many users [filament:*common*] cooling = 0 @@ -235,47 +208,47 @@ filament_type = PLA filament_density = 1.24 filament_cost = 20 first_layer_bed_temperature = 60 -first_layer_temperature = 210 +first_layer_temperature = 230 fan_always_on = 1 max_fan_speed = 100 min_fan_speed = 100 bridge_fan_speed = 100 -disable_fan_first_layers = 2 -temperature = 205 +disable_fan_first_layers = 1 +temperature = 200 [filament:*PLA+*] inherits = *common* bed_temperature = 60 fan_below_layer_time = 100 filament_colour = #DDDDDD -filament_type = PLA +filament_type = PLA+ filament_density = 1.24 filament_cost = 20 first_layer_bed_temperature = 60 -first_layer_temperature = 220 +first_layer_temperature = 230 fan_always_on = 1 max_fan_speed = 100 min_fan_speed = 100 bridge_fan_speed = 100 -disable_fan_first_layers = 2 -temperature = 210 +disable_fan_first_layers = 1 +temperature = 200 [filament:*PET*] inherits = *common* -bed_temperature = 70 +bed_temperature = 80 disable_fan_first_layers = 2 fan_below_layer_time = 20 filament_colour = #DDDDDD filament_type = PETG filament_density = 1.27 filament_cost = 30 -first_layer_bed_temperature = 70 -first_layer_temperature = 240 +first_layer_bed_temperature = 80 +first_layer_temperature = 260 fan_always_on = 1 max_fan_speed = 50 min_fan_speed = 50 bridge_fan_speed = 100 -temperature = 240 +temperature = 260 [filament:*ABS*] inherits = *common* @@ -286,14 +259,14 @@ filament_colour = #DDDDDD filament_type = ABS filament_density = 1.04 filament_cost = 20 -first_layer_bed_temperature = 100 -first_layer_temperature = 245 +first_layer_bed_temperature = 90 +first_layer_temperature = 260 fan_always_on = 0 max_fan_speed = 0 min_fan_speed = 0 bridge_fan_speed = 30 top_fan_speed = 0 -temperature = 245 +temperature = 260 [filament:Generic PLA @ANKER] inherits = *PLA* @@ -324,8 +297,8 @@ deretract_speed = 60 extruder_colour = #FCE94F extruder_offset = 0x0 gcode_flavor = marlin -silent_mode = 0 -remaining_times = 0 +silent_mode = 1 +remaining_times = 1 machine_max_acceleration_e = 2500 machine_max_acceleration_extruding = 2500 machine_max_acceleration_retracting = 2500 @@ -338,8 +311,8 @@ machine_max_feedrate_x = 300 machine_max_feedrate_y = 300 machine_max_feedrate_z = 20 machine_max_jerk_e = 3 -machine_max_jerk_x = 30 -machine_max_jerk_y = 30 +machine_max_jerk_x = 15 +machine_max_jerk_y = 15 machine_max_jerk_z = 0.3 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 @@ -347,10 +320,10 @@ layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z} max_print_height = 250 printer_notes = printer_settings_id = -retract_before_travel = 2 -retract_before_wipe = 70% +retract_before_travel = 3 +retract_before_wipe = 0 retract_layer_change = 1 -retract_length_toolchange = 1 +retract_length_toolchange = 4 retract_lift = 0 retract_lift_above = 0 retract_lift_below = 0 @@ -365,10 +338,10 @@ use_firmware_retraction = 0 use_relative_e_distances = 0 use_volumetric_e = 0 variable_layer_height = 1 -wipe = 1 +wipe = 0 z_offset = 0 -default_filament_profile = "Generic PLA+ @ANKER" -start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for nozzle temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime) +default_filament_profile = Generic PLA+ @ANKER +start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nM420 S1; restore saved Auto Bed Leveling data\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime) end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84 [printer:*M5*] @@ -376,12 +349,12 @@ inherits = *common* bed_shape = 0x0,235-0,235x235,0x235 max_print_height = 250 printer_model = M5 -retract_length = 1.25 +retract_length = 3 retract_speed = 60 deretract_speed = 60 -retract_before_travel = 1 +retract_before_travel = 3 retract_before_wipe = 0% -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_ANKERMAKE\nPRINTER_MODEL_M5 +printer_notes = Don 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_ANKERMAKE\nPRINTER_MODEL_M5 [printer:AnkerMake M5 (0.4 mm nozzle)] inherits = *M5* @@ -389,5 +362,5 @@ nozzle_diameter = 0.4 printer_variant = 0.4 min_layer_height = 0.08 max_layer_height = 0.32 -retract_lift_above = 0.2 -default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER" \ No newline at end of file +retract_lift_above = 0 +default_print_profile = 0.2 mm OPTIMAL (0.4 mm nozzle) @ANKER From 16024b12c523e194f32c9ebf438442ceaeb9375c Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:51:51 +0100 Subject: [PATCH 098/201] Update Anker.idx --- resources/profiles/Anker.idx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/profiles/Anker.idx b/resources/profiles/Anker.idx index f96cee247..0cf915db1 100644 --- a/resources/profiles/Anker.idx +++ b/resources/profiles/Anker.idx @@ -1,3 +1,5 @@ min_slic3r_version = 2.6.0-alpha4 1.1.1 Initial official version 1.0.1 Initial Version +min_slic3r_version = 2.6.0-alpha1 +1.0.0 Initial Version From 79adb72a2529a56dd9cca1628298b4b6bcea1427 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Mar 2023 17:13:57 +0100 Subject: [PATCH 099/201] Fix int overflow, reduces amount of bridging substantially, improve code description, extend bridge anchors when anchoring to solid surfaces on lower layers --- src/libslic3r/PrintObject.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b72a2bacb..86cb16e2d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1618,7 +1618,8 @@ void PrintObject::bridge_over_infill() if (layer->lower_layer == nullptr) { continue; } - auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); + double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); + // unsupported area will serve as a filter for polygons worth bridging. Polygons unsupported_area; Polygons lower_layer_solids; bool contains_only_lightning = true; @@ -1627,6 +1628,7 @@ void PrintObject::bridge_over_infill() contains_only_lightning = false; } Polygons fill_polys = to_polygons(region->fill_expolygons()); + // initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end()); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { @@ -1636,25 +1638,30 @@ void PrintObject::bridge_over_infill() } } unsupported_area = closing(unsupported_area, SCALED_EPSILON); - - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - unsupported_area = shrink(unsupported_area, 4 * spacing); + // By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids + // NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole + lower_layer_solids = expand(lower_layer_solids, 3 * spacing); + // By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters. + unsupported_area = shrink(unsupported_area, 3 * spacing); unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. + // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; - if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { - Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); + if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing)); + // after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) { - auto area = p.area(); + double area = p.area(); if (area < spacing * scale_(12.0) && area > spacing * spacing) { worth_bridging.push_back(p); } } - worth_bridging = intersection(closing(worth_bridging, 2 * spacing), s->expolygon); + worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon); candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -1663,7 +1670,7 @@ void PrintObject::bridge_over_infill() to_lines(unsupported_area)); #endif #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(s->expolygon)), + debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)), to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))), to_lines(unsupported_area)); @@ -1728,15 +1735,15 @@ void PrintObject::bridge_over_infill() layer_area_covered_by_candidates[pair.first] = {}; } + // prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise) + // and if the inflated AABB polygons overlap somewhere tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, &layer_area_covered_by_candidates]( tbb::blocked_range r) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { size_t lidx = layers_with_candidates[job_idx]; for (const auto &candidate : surfaces_by_layer.at(lidx)) { - Polygon candiate_inflated_aabb = get_extents(candidate.new_polys) - .inflated(candidate.region->flow(frSolidInfill, true).scaled_spacing() * 5) - .polygon(); + Polygon candiate_inflated_aabb = get_extents(candidate.new_polys).inflated(scale_(7)).polygon(); layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx), Polygons{candiate_inflated_aabb}); } @@ -2088,13 +2095,13 @@ void PrintObject::bridge_over_infill() } } - deep_infill_area = expand(deep_infill_area, spacing); + deep_infill_area = expand(deep_infill_area, spacing * 1.5); // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors Polygons expansion_area; Polygons total_fill_area; for (const LayerRegion *region : layer->regions()) { - Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); + Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_types({stInternal, stInternalSolid})); expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end()); Polygons fill_polys = to_polygons(region->fill_expolygons()); total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end()); From 39bca4420c88ae8afd4221acd266c42284d401b3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 17 Mar 2023 17:13:29 +0100 Subject: [PATCH 100/201] Fix for #10072 - Split to objects is acting funky --- src/slic3r/GUI/Plater.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e89dd2624..a64de49ee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3160,6 +3160,8 @@ void Plater::priv::split_object() // causing original positions not to be kept std::vector idxs = load_model_objects(new_objects); + // clear previosli selection + get_selection().clear(); // select newly added objects for (size_t idx : idxs) { From b9d8fe7118d03a607bff5acae3e5acb25ed666e8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 07:48:26 +0100 Subject: [PATCH 101/201] WIP: PlaceholderParser support for writable output variables. --- src/libslic3r/PlaceholderParser.cpp | 122 +++++++++++++++++++- src/libslic3r/PlaceholderParser.hpp | 6 +- tests/libslic3r/test_placeholder_parser.cpp | 13 +++ 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 5235fd72e..c26197d27 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -171,7 +171,8 @@ namespace client struct OptWithPos { OptWithPos() {} OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range) : opt(opt), it_range(it_range) {} - ConfigOptionConstPtr opt = nullptr; + ConfigOptionConstPtr opt { nullptr }; + bool writable { false }; boost::iterator_range it_range; }; @@ -688,6 +689,7 @@ namespace client const DynamicConfig *external_config = nullptr; const DynamicConfig *config = nullptr; const DynamicConfig *config_override = nullptr; + mutable DynamicConfig *config_outputs = nullptr; size_t current_extruder_id = 0; PlaceholderParser::ContextData *context_data = nullptr; // If false, the macro_processor will evaluate a full macro. @@ -713,6 +715,7 @@ namespace client } const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } + ConfigOption* resolve_output_symbol(const std::string &opt_key) const { return this->config_outputs ? this->config_outputs->optptr(opt_key, false) : nullptr; } template static void legacy_variable_expansion( @@ -788,8 +791,12 @@ namespace client OptWithPos &output) { const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); - if (opt == nullptr) - ctx->throw_exception("Not a variable name", opt_key); + if (opt == nullptr) { + opt = ctx->resolve_output_symbol(std::string(opt_key.begin(), opt_key.end())); + if (opt == nullptr) + ctx->throw_exception("Not a variable name", opt_key); + output.writable = true; + } output.opt = opt; output.it_range = opt_key; } @@ -914,6 +921,98 @@ namespace client output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); } + // Decoding a scalar variable symbol "opt", assigning it a value of "param". + template + static void scalar_variable_assign( + const MyContext *ctx, + OptWithPos &opt, + expr ¶m, + // Not used, just clear it. + std::string &out) + { + if (! opt.writable) + ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + if (opt.opt->is_vector()) + ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); + ConfigOption *wropt = const_cast(opt.opt); + switch (wropt->type()) { + case coFloat: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coInt: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_i(); + break; + case coString: + static_cast(wropt)->value = param.to_string(); + break; + case coPercent: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coBool: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(wropt)->value = param.b(); + break; + default: + ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); + } + out.clear(); + } + + template + static void vector_variable_assign( + const MyContext *ctx, + OptWithPos &opt, + int &index, + expr ¶m, + // Not used, just clear it. + std::string &out) + { + if (! opt.writable) + ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + if (opt.opt->is_scalar()) + ctx->throw_exception("Referencing an output scalar variable when vector is expected", opt.it_range); + ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + if (index < 0 || index >= int(vec->size())) + ctx->throw_exception("Index out of range", opt.it_range); + switch (opt.opt->type()) { + case coFloats: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[index] = param.as_d(); + break; + case coInts: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[index] = param.as_i(); + break; + case coStrings: + static_cast(vec)->values[index] = param.to_string(); + break; + case coPercents: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[index] = param.as_d(); + break; + case coBools: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(vec)->values[index] = param.b(); + break; + default: + ctx->throw_exception("Unsupported output vector variable type", opt.it_range); + } + out.clear(); + } + // Verify that the expression returns an integer, which may be used // to address a vector. template @@ -1165,7 +1264,9 @@ namespace client macro = (kw["if"] > if_else_output(_r1) [_val = _1]) // | (kw["switch"] > switch_output(_r1) [_val = _1]) - | additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]; + | (assignment_statement(_r1) [_val = _1]) + | (additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + ; macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). @@ -1257,6 +1358,15 @@ namespace client ); multiplicative_expression.name("multiplicative_expression"); + assignment_statement = + variable_reference(_r1)[_a = _1] >> + ( + ('[' >> additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] >> ']' >> '=' >> additive_expression(_r1)) + [px::bind(&MyContext::vector_variable_assign, _r1, _a, _b, _2, _val)] + | ('=' >> additive_expression(_r1)) + [px::bind(&MyContext::scalar_variable_assign, _r1, _a, _1, _val)] + ); + struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) { out.it_range = boost::iterator_range(start_pos, start_pos); } @@ -1430,6 +1540,7 @@ namespace client qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> is_nil_test; qi::rule, spirit_encoding::space_type> if_else_output; + qi::rule, int>, spirit_encoding::space_type> assignment_statement; // qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; qi::symbols keywords; @@ -1461,12 +1572,13 @@ static std::string process_macro(const std::string &templ, client::MyContext &co return output; } -std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context_data) const +std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context_data) const { client::MyContext context; context.external_config = this->external_config(); context.config = &this->config(); context.config_override = config_override; + context.config_outputs = config_outputs; context.current_extruder_id = current_extruder_id; context.context_data = context_data; return process_macro(templ, context); diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index fc184be77..a3f051558 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -55,8 +55,10 @@ public: // Fill in the template using a macro processing language. // Throws Slic3r::PlaceholderParserError on syntax or runtime error. - std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const; - + std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context) const; + std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const + { return this->process(templ, current_extruder_id, config_override, nullptr /* config_outputs */, context); } + // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax. // Throws Slic3r::PlaceholderParserError on syntax or runtime error. static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr); diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 8ad6b243f..5248e089a 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -117,4 +117,17 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); } SECTION("enum expression") { REQUIRE(boolean_expression("gcode_flavor == \"marlin\"")); } + + SECTION("write to a scalar variable") { + DynamicConfig config_outputs; + config_outputs.set_key_value("writable_string", new ConfigOptionString()); + parser.process("{writable_string = \"Written\"}", 0, nullptr, &config_outputs, nullptr); + REQUIRE(parser.process("{writable_string}", 0, nullptr, &config_outputs, nullptr) == "Written"); + } + SECTION("write to a vector variable") { + DynamicConfig config_outputs; + config_outputs.set_key_value("writable_floats", new ConfigOptionFloats({ 0., 0., 0. })); + parser.process("{writable_floats[1] = 33}", 0, nullptr, &config_outputs, nullptr); + REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.)); + } } From d152b67ce5512c598d6632ddba255a4c93c351a2 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 10:25:52 +0100 Subject: [PATCH 102/201] PlaceholderParser: Simplified the parser after introducing the writable variables. --- src/libslic3r/PlaceholderParser.cpp | 395 +++++++++++++--------------- 1 file changed, 187 insertions(+), 208 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index c26197d27..cb0760d4d 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -173,7 +173,11 @@ namespace client OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range) : opt(opt), it_range(it_range) {} ConfigOptionConstPtr opt { nullptr }; bool writable { false }; + // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. + int index { -1 }; boost::iterator_range it_range; + + bool has_index() const { return index != -1; } }; template @@ -802,128 +806,127 @@ namespace client } template - static void scalar_variable_reference( + static void store_variable_index( const MyContext *ctx, - OptWithPos &opt, - expr &output) + OptWithPos &opt, + int index, + Iterator it_end, + OptWithPos &output) { - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); - switch (opt.opt->type()) { - case coFloat: output.set_d(opt.opt->getFloat()); break; - case coInt: output.set_i(opt.opt->getInt()); break; - case coString: output.set_s(static_cast(opt.opt)->value); break; - case coPercent: output.set_d(opt.opt->getFloat()); break; - case coEnum: - case coPoint: output.set_s(opt.opt->serialize()); break; - case coBool: output.set_b(opt.opt->getBool()); break; - case coFloatOrPercent: - { - std::string opt_key(opt.it_range.begin(), opt.it_range.end()); - if (boost::ends_with(opt_key, "extrusion_width")) { - // Extrusion width supports defaults and a complex graph of dependencies. - output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); - } else if (! static_cast(opt.opt)->percent) { - // Not a percent, just return the value. - output.set_d(opt.opt->getFloat()); - } else { - // Resolve dependencies using the "ratio_over" link to a parent value. - const ConfigOptionDef *opt_def = print_config_def.get(opt_key); - assert(opt_def != nullptr); - double v = opt.opt->getFloat() * 0.01; // percent to ratio - for (;;) { - const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); - if (opt_parent == nullptr) - ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); - if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { - // Extrusion width supports defaults and a complex graph of dependencies. - assert(opt_parent->type() == coFloatOrPercent); - v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); - break; - } - if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { - v *= opt_parent->getFloat(); - if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) - break; - v *= 0.01; // percent to ratio - } - // Continue one level up in the "ratio_over" hierarchy. - opt_def = print_config_def.get(opt_def->ratio_over); - assert(opt_def != nullptr); - } - output.set_d(v); - } - break; - } - default: - ctx->throw_exception("Unknown scalar variable type", opt.it_range); - } - output.it_range = opt.it_range; + if (! opt.opt->is_vector()) + ctx->throw_exception("Cannot index a scalar variable", opt.it_range); + if (index < 0) + ctx->throw_exception("Referencing a vector variable with a negative index", opt.it_range); + output = opt; + output.index = index; + output.it_range.end() = it_end; } template - static void vector_variable_reference( + static void variable_value( const MyContext *ctx, OptWithPos &opt, - int &index, - Iterator it_end, expr &output) { - if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); - const ConfigOptionVectorBase *vec = static_cast(opt.opt); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); - switch (opt.opt->type()) { - case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; - case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; - case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; - case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; - //case coEnums: output.set_s(opt.opt->vserialize()[idx]); break; - default: - ctx->throw_exception("Unknown vector variable type", opt.it_range); + if (opt.opt->is_vector()) { + if (! opt.has_index()) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + const ConfigOptionVectorBase *vec = static_cast(opt.opt); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index); + switch (opt.opt->type()) { + case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; + case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; + case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; + case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; + //case coEnums: output.set_s(opt.opt->vserialize()[idx]); break; + default: + ctx->throw_exception("Unknown vector variable type", opt.it_range); + } + } else { + assert(opt.opt->is_scalar()); + switch (opt.opt->type()) { + case coFloat: output.set_d(opt.opt->getFloat()); break; + case coInt: output.set_i(opt.opt->getInt()); break; + case coString: output.set_s(static_cast(opt.opt)->value); break; + case coPercent: output.set_d(opt.opt->getFloat()); break; + case coEnum: + case coPoint: output.set_s(opt.opt->serialize()); break; + case coBool: output.set_b(opt.opt->getBool()); break; + case coFloatOrPercent: + { + std::string opt_key(opt.it_range.begin(), opt.it_range.end()); + if (boost::ends_with(opt_key, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); + } else if (! static_cast(opt.opt)->percent) { + // Not a percent, just return the value. + output.set_d(opt.opt->getFloat()); + } else { + // Resolve dependencies using the "ratio_over" link to a parent value. + const ConfigOptionDef *opt_def = print_config_def.get(opt_key); + assert(opt_def != nullptr); + double v = opt.opt->getFloat() * 0.01; // percent to ratio + for (;;) { + const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); + if (opt_parent == nullptr) + ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); + if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + assert(opt_parent->type() == coFloatOrPercent); + v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); + break; + } + if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { + v *= opt_parent->getFloat(); + if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) + break; + v *= 0.01; // percent to ratio + } + // Continue one level up in the "ratio_over" hierarchy. + opt_def = print_config_def.get(opt_def->ratio_over); + assert(opt_def != nullptr); + } + output.set_d(v); + } + break; + } + default: + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + } } - output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); + + output.it_range = opt.it_range; } // Return a boolean value, true if the scalar variable referenced by "opt" is nullable and it has a nil value. - template - static void is_nil_test_scalar( - const MyContext *ctx, - OptWithPos &opt, - expr &output) - { - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); - output.set_b(opt.opt->is_nil()); - output.it_range = opt.it_range; - } - // Return a boolean value, true if an element of a vector variable referenced by "opt[index]" is nullable and it has a nil value. template - static void is_nil_test_vector( + static void is_nil_test( const MyContext *ctx, OptWithPos &opt, - int &index, - Iterator it_end, expr &output) { - if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); - const ConfigOptionVectorBase *vec = static_cast(opt.opt); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); - output.set_b(static_cast(opt.opt)->is_nil(idx)); - output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); + if (opt.opt->is_vector()) { + if (! opt.has_index()) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + const ConfigOptionVectorBase *vec = static_cast(opt.opt); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + output.set_b(static_cast(opt.opt)->is_nil(opt.index >= int(vec->size()) ? 0 : size_t(opt.index))); + } else { + assert(opt.opt->is_scalar()); + output.set_b(opt.opt->is_nil()); + } + output.it_range = opt.it_range; } // Decoding a scalar variable symbol "opt", assigning it a value of "param". template - static void scalar_variable_assign( + static void variable_assign( const MyContext *ctx, OptWithPos &opt, expr ¶m, @@ -932,83 +935,71 @@ namespace client { if (! opt.writable) ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); - ConfigOption *wropt = const_cast(opt.opt); - switch (wropt->type()) { - case coFloat: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(wropt)->value = param.as_d(); - break; - case coInt: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(wropt)->value = param.as_i(); - break; - case coString: - static_cast(wropt)->value = param.to_string(); - break; - case coPercent: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(wropt)->value = param.as_d(); - break; - case coBool: - if (param.type() != expr::TYPE_BOOL) - ctx->throw_exception("Right side is not a boolean expression", param.it_range); - static_cast(wropt)->value = param.b(); - break; - default: - ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); - } - out.clear(); - } - - template - static void vector_variable_assign( - const MyContext *ctx, - OptWithPos &opt, - int &index, - expr ¶m, - // Not used, just clear it. - std::string &out) - { - if (! opt.writable) - ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); - if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing an output scalar variable when vector is expected", opt.it_range); - ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - if (index < 0 || index >= int(vec->size())) - ctx->throw_exception("Index out of range", opt.it_range); - switch (opt.opt->type()) { - case coFloats: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(vec)->values[index] = param.as_d(); - break; - case coInts: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(vec)->values[index] = param.as_i(); - break; - case coStrings: - static_cast(vec)->values[index] = param.to_string(); - break; - case coPercents: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(vec)->values[index] = param.as_d(); - break; - case coBools: - if (param.type() != expr::TYPE_BOOL) - ctx->throw_exception("Right side is not a boolean expression", param.it_range); - static_cast(vec)->values[index] = param.b(); - break; - default: - ctx->throw_exception("Unsupported output vector variable type", opt.it_range); + if (opt.opt->is_vector()) { + if (! opt.has_index()) + ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); + ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + if (opt.index >= int(vec->size())) + ctx->throw_exception("Index out of range", opt.it_range); + switch (opt.opt->type()) { + case coFloats: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[opt.index] = param.as_d(); + break; + case coInts: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[opt.index] = param.as_i(); + break; + case coStrings: + static_cast(vec)->values[opt.index] = param.to_string(); + break; + case coPercents: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[opt.index] = param.as_d(); + break; + case coBools: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(vec)->values[opt.index] = param.b(); + break; + default: + ctx->throw_exception("Unsupported output vector variable type", opt.it_range); + } + } else { + assert(opt.opt->is_scalar()); + ConfigOption *wropt = const_cast(opt.opt); + switch (wropt->type()) { + case coFloat: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coInt: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_i(); + break; + case coString: + static_cast(wropt)->value = param.to_string(); + break; + case coPercent: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coBool: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(wropt)->value = param.b(); + break; + default: + ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); + } } out.clear(); } @@ -1110,9 +1101,9 @@ namespace client { "multiplicative_expression", "Expecting an expression." }, { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, - { "scalar_variable_reference", "Expecting a scalar variable reference."}, - { "is_nil_test", "Expecting a scalar variable reference."}, { "variable_reference", "Expecting a variable reference."}, + { "is_nil_test", "Expecting a scalar variable reference."}, + { "variable", "Expecting a variable name."}, { "regular_expression", "Expecting a regular expression."} }; @@ -1359,13 +1350,8 @@ namespace client multiplicative_expression.name("multiplicative_expression"); assignment_statement = - variable_reference(_r1)[_a = _1] >> - ( - ('[' >> additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] >> ']' >> '=' >> additive_expression(_r1)) - [px::bind(&MyContext::vector_variable_assign, _r1, _a, _b, _2, _val)] - | ('=' >> additive_expression(_r1)) - [px::bind(&MyContext::scalar_variable_assign, _r1, _a, _1, _val)] - ); + (variable_reference(_r1) >> '=' > additive_expression(_r1)) + [px::bind(&MyContext::variable_assign, _r1, _1, _2, _val)]; struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) @@ -1392,7 +1378,7 @@ namespace client static void noexpr(expr &out) { out.reset(); } }; unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( - scalar_variable_reference(_r1) [ _val = _1 ] + variable_reference(_r1) [px::bind(&MyContext::variable_value, _r1, _1, _val)] | (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] @@ -1424,27 +1410,20 @@ namespace client ); optional_parameter.name("optional_parameter"); - scalar_variable_reference = - variable_reference(_r1)[_a=_1] >> - ( - ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > - iter_pos[px::bind(&MyContext::vector_variable_reference, _r1, _a, _b, _1, _val)]) - | eps[px::bind(&MyContext::scalar_variable_reference, _r1, _a, _val)] - ); - scalar_variable_reference.name("scalar variable reference"); + is_nil_test = variable_reference(_r1)[px::bind(&MyContext::is_nil_test, _r1, _1, _val)]; + is_nil_test.name("is_nil test"); - variable_reference = identifier - [ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + variable_reference = + variable(_r1)[_a=_1] >> + ( + ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > iter_pos) + [px::bind(&MyContext::store_variable_index, _r1, _a, _b, _2, _val)] + | eps[_val=_a] + ); variable_reference.name("variable reference"); - is_nil_test = - variable_reference(_r1)[_a=_1] >> - ( - ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > - iter_pos[px::bind(&MyContext::is_nil_test_vector, _r1, _a, _b, _1, _val)]) - | eps[px::bind(&MyContext::is_nil_test_scalar, _r1, _a, _val)] - ); - is_nil_test.name("is_nil test"); + variable = identifier[ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + variable.name("variable reference"); regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']]; regular_expression.name("regular_expression"); @@ -1488,8 +1467,8 @@ namespace client debug(multiplicative_expression); debug(unary_expression); debug(optional_parameter); - debug(scalar_variable_reference); debug(variable_reference); + debug(variable); debug(is_nil_test); debug(regular_expression); } @@ -1533,11 +1512,11 @@ namespace client // Evaluate boolean expression into bool. qi::rule bool_expr_eval; // Reference of a scalar variable, or reference to a field of a vector variable. - qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> scalar_variable_reference; + qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> variable_reference; // Rule to translate an identifier to a ConfigOption, or to fail. - qi::rule(const MyContext*), spirit_encoding::space_type> variable_reference; + qi::rule(const MyContext*), spirit_encoding::space_type> variable; // Evaluating whether a nullable variable is nil. - qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> is_nil_test; + qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; From 5ab9532e394238b9a580b8c10fe52abcec7b439e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 13:45:41 +0100 Subject: [PATCH 103/201] Fix bad actualization without mesh source sorting --- src/slic3r/Utils/RaycastManager.cpp | 211 +++++++++++++++------------- 1 file changed, 110 insertions(+), 101 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 80a716755..62722b076 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -1,25 +1,31 @@ #include "RaycastManager.hpp" #include +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + using namespace Slic3r::GUI; -namespace priv { +namespace{ using namespace Slic3r; -static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); -static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); -static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ +void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); +const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); +RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ return std::make_pair(instance.id().id, volume.id().id); } -static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); -static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { +RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); } -static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { +bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; +template inline void erase(std::vector &vec, const std::vector &flags); } void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object.volumes, skip, meshes); + ::actualize(m_meshes, object.volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -32,8 +38,8 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes for (const ModelInstance *instance : object.instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(*volume, *instance); - auto item = priv::find(m_transformations, key); + TrKey key = ::create_key(*volume, *instance); + auto item = ::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -41,19 +47,17 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(key, transformation)); + m_transformations.emplace_back(key, transformation); need_sort = true; } } } // clean other transformation - for (int i = removed_transf.size() - 1; i >= 0; --i) - if (removed_transf[i]) - m_transformations.erase(m_transformations.begin() + i); + ::erase(m_transformations, removed_transf); if (need_sort) - std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); + std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower); } void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes) @@ -61,7 +65,7 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, const ModelVolumePtrs &volumes = instance.get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_meshes, volumes, skip, meshes); + ::actualize(m_meshes, volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -74,8 +78,8 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, const Transform3d &volume_tr = volume->get_matrix(); const Transform3d &instrance_tr = instance.get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(*volume, instance); - auto item = priv::find(m_transformations, key); + TrKey key = ::create_key(*volume, instance); + auto item = ::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -83,40 +87,35 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(key, transformation)); + m_transformations.emplace_back(key, transformation); need_sort = true; } } // clean other transformation - for (int i = removed_transf.size() - 1; i >= 0; --i) - if (removed_transf[i]) - m_transformations.erase(m_transformations.begin() + i); + ::erase(m_transformations, removed_transf); if (need_sort) - std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); + std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower); } std::optional RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const { // Improve: it is not neccessaru to use AABBMesh and calc normal for every hit - struct Result - { - const AABBMesh *mesh = nullptr; - double squared_distance; - int face; - Vec3d hit_world; - const Transform3d *tramsformation; - const TrKey *key; - }result; + + // Results + const AABBMesh *hit_mesh = nullptr; + double hit_squared_distance = 0.; + int hit_face = -1; + Vec3d hit_world; + const Transform3d *hit_tramsformation = nullptr; + const TrKey *hit_key = nullptr; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation]: m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; - const Transform3d &transformation = item.second; Transform3d inv = transformation.inverse(); // transform input into mesh world @@ -129,46 +128,44 @@ std::optional RaycastManager::first_hit(const Vec3d& point, const AABBMesh::hit_result &hit = hits.front(); // convert to world - Vec3d hit_world = transformation * hit.position(); - double squared_distance = (point - hit_world).squaredNorm(); - if (result.mesh != nullptr && - result.squared_distance < squared_distance) + Vec3d world = transformation * hit.position(); + double squared_distance = (point - world).squaredNorm(); + if (hit_mesh != nullptr && + hit_squared_distance < squared_distance) continue; // exist closer one - result.mesh = mesh; - result.squared_distance = squared_distance; - result.face = hit.face(); - result.hit_world = hit_world; - result.tramsformation = &transformation; - result.key = &key; + hit_mesh = mesh; + hit_squared_distance = squared_distance; + hit_face = hit.face(); + hit_world = world; + hit_tramsformation = &transformation; + hit_key = &key; } - if (result.mesh == nullptr) + if (hit_mesh == nullptr) return {}; // Calculate normal from transformed triangle // NOTE: Anisotropic transformation of normal is not perpendiculat to triangle - const Vec3i tri = result.mesh->indices(result.face); - Vec3d pts[3]; - auto tr = result.tramsformation->linear(); + const Vec3i tri = hit_mesh->indices(hit_face); + std::array pts; + auto tr = hit_tramsformation->linear(); for (int i = 0; i < 3; ++i) - pts[i] = tr * result.mesh->vertices(tri[i]).cast(); + pts[i] = tr * hit_mesh->vertices(tri[i]).cast(); Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); normal_world.normalize(); - SurfacePoint point_world{result.hit_world, normal_world}; - return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; + SurfacePoint point_world{hit_world, normal_world}; + return RaycastManager::Hit{point_world, *hit_key, hit_squared_distance}; } std::optional RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const { std::optional closest; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation] : m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const Transform3d &transformation = item.second; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; @@ -196,14 +193,12 @@ std::optional RaycastManager::closest_hit(const Vec3d &poin std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { std::optional closest; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation] : m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; - const Transform3d &transformation = item.second; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; @@ -222,17 +217,15 @@ std::optional RaycastManager::closest(const Vec3d &p } Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { - // TODO: transformations are sorted use lower bound - auto item = std::find_if(m_transformations.begin(), - m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { - return it.first == tr_key; - }); - if (item == m_transformations.end()) return Transform3d::Identity(); - return item->second; + auto tr = ::find(m_transformations, tr_key); + if (tr == m_transformations.end()) + return Transform3d::Identity(); + return tr->second; } -void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) + +namespace { +void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) { // check if volume was removed std::vector removed_meshes(meshes.size(), {true}); @@ -242,33 +235,33 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu size_t oid = volume->id().id; if (skip != nullptr && skip->skip(oid)) continue; - auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); - if (item == meshes.end()) { - // exist AABB in inputs ? - if (inputs != nullptr) { - auto input = std::find_if(inputs->begin(), inputs->end(), - [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); - if (input != inputs->end()) { - meshes.emplace_back(std::move(*input)); - continue; - } - } - - // add new raycaster - bool calculate_epsilon = true; - auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); - meshes.emplace_back(std::make_pair(oid, std::move(mesh))); - need_sort = true; - } else { + auto is_oid = [oid](const RaycastManager::Mesh &it) { return oid == it.first; }; + if (auto item = std::find_if(meshes.begin(), meshes.end(), is_oid); + item != meshes.end()) { size_t index = item - meshes.begin(); removed_meshes[index] = false; + continue; } + + // exist AABB in inputs ? + if (inputs != nullptr) { + auto input = std::find_if(inputs->begin(), inputs->end(), is_oid); + if (input != inputs->end()) { + meshes.emplace_back(std::move(*input)); + need_sort = true; + continue; + } + } + + // add new raycaster + bool calculate_epsilon = true; + auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); + meshes.emplace_back(std::make_pair(oid, std::move(mesh))); + need_sort = true; } // clean other raycasters - for (int i = removed_meshes.size() - 1; i >= 0; --i) - if (removed_meshes[i]) - meshes.erase(meshes.begin() + i); + erase(meshes, removed_meshes); // All the time meshes must be sorted by volume id - for faster search if (need_sort) { @@ -277,28 +270,44 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu } } -const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) +const Slic3r::AABBMesh *get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) { - auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; + auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) { return m.first < i; }; auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index); if (it == meshes.end() || it->first != volume_id) return nullptr; return &(*(it->second)); } -RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { - auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool { - return priv::is_lower_key(it.first, key); - }; +RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); }; auto it = std::lower_bound(items.begin(), items.end(), key, fnc); - if (it == items.end() || it->first != key) + if (it != items.end() && it->first != key) return items.end(); return it; } -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/CameraUtils.hpp" +RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key) +{ + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); }; + auto it = std::lower_bound(items.begin(), items.end(), key, fnc); + if (it != items.end() && it->first != key) + return items.end(); + return it; +} + +template inline void erase(std::vector &vec, const std::vector &flags) +{ + assert(vec.size() == flags.size()); + if (flags.empty()) return; + + // reverse iteration over flags to erase indices from back to front. + for (int i = static_cast(flags.size()) - 1; i >= 0; --i) + if (flags[i]) + vec.erase(vec.begin() + i); +} + +} // namespace namespace Slic3r::GUI{ From 1b8553c747ad5c0eb9bdba46d2b7c8003ef9ad5f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Mar 2023 14:34:04 +0100 Subject: [PATCH 104/201] Reintroduced visualization of SLA supports and pad into 3D scene and gizmos Hollow and SLA support as it was in 2.5.0 --- src/slic3r/GUI/3DScene.cpp | 61 +++++++ src/slic3r/GUI/3DScene.hpp | 10 ++ src/slic3r/GUI/GLCanvas3D.cpp | 160 ++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 9 ++ src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp | 67 ++++++-- src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 97 +++++------ src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 4 +- 9 files changed, 337 insertions(+), 82 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index c25bff45d..843aa6a33 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -564,6 +564,67 @@ int GLVolumeCollection::load_wipe_tower_preview( return int(volumes.size() - 1); } +// Load SLA auxiliary GLVolumes (for support trees or pad). +// This function produces volumes for multiple instances in a single shot, +// as some object specific mesh conversions may be expensive. +void GLVolumeCollection::load_object_auxiliary( + const SLAPrintObject* print_object, + int obj_idx, + // pairs of + const std::vector>& instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp) +{ + if (print_object->get_mesh_to_print() == nullptr) + return; + const Transform3d mesh_trafo_inv = print_object->trafo().inverse(); + + auto add_volume = [this, &instances, timestamp](int obj_idx, int inst_idx, const ModelInstance& model_instance, SLAPrintObjectStep step, + const TriangleMesh& mesh, const ColorRGBA& color, std::optional convex_hull = std::nullopt) { + if (mesh.empty()) + return; + + GLVolume& v = *this->volumes.emplace_back(new GLVolume(color)); +#if ENABLE_SMOOTH_NORMALS + v.model.init_from(mesh, true); +#else + v.model.init_from(mesh); + v.model.set_color(color); + v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_SMOOTH_NORMALS + v.composite_id = GLVolume::CompositeID(obj_idx, -int(step), inst_idx); + v.geometry_id = std::pair(timestamp, model_instance.id().id); + if (convex_hull.has_value()) + v.set_convex_hull(*convex_hull); + v.is_modifier = false; + v.shader_outside_printer_detection_enabled = (step == slaposSupportTree); + v.set_instance_transformation(model_instance.get_transformation()); + }; + + // Get the support mesh. + TriangleMesh supports_mesh = print_object->support_mesh(); + if (!supports_mesh.empty()) { + supports_mesh.transform(mesh_trafo_inv); + TriangleMesh convex_hull = supports_mesh.convex_hull_3d(); + for (const std::pair& instance_idx : instances) { + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); + } + } + + // Get the pad mesh. + TriangleMesh pad_mesh = print_object->pad_mesh(); + if (!pad_mesh.empty()) { + pad_mesh.transform(mesh_trafo_inv); + TriangleMesh convex_hull = pad_mesh.convex_hull_3d(); + for (const std::pair& instance_idx : instances) { + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposPad, pad_mesh, GLVolume::SLA_PAD_COLOR, convex_hull); + } + } +} + GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba) { GLVolume* out = new_nontoolpath_volume(rgba); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 9791a0faf..9095885fc 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -430,6 +430,16 @@ public: float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); #endif // ENABLE_OPENGL_ES + // Load SLA auxiliary GLVolumes (for support trees or pad). + void load_object_auxiliary( + const SLAPrintObject* print_object, + int obj_idx, + // pairs of + const std::vector>& instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp); + GLVolume* new_toolpath_volume(const ColorRGBA& rgba); GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); // Render the volumes by OpenGL. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1f2f668c3..ea79739c4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1881,6 +1881,15 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volume_idx; }; + // SLA steps to pull the preview meshes for. + typedef std::array SLASteps; + SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; + struct SLASupportState { + std::array::value> step; + }; + // State of the sla_steps for all SLAPrintObjects. + std::vector sla_support_state; + std::vector instance_ids_selected; std::vector map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); std::vector deleted_volumes; @@ -1906,6 +1915,37 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } + if (printer_technology == ptSLA) { + const SLAPrint* sla_print = this->sla_print(); +#ifndef NDEBUG + // Verify that the SLAPrint object is synchronized with m_model. + check_model_ids_equal(*m_model, sla_print->model()); +#endif // NDEBUG + sla_support_state.reserve(sla_print->objects().size()); + for (const SLAPrintObject* print_object : sla_print->objects()) { + 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::State::Done) { + std::shared_ptr m = print_object->get_mesh_to_print(); + if (m == nullptr || m->empty()) + // Consider the DONE step without a valid mesh as invalid for the purpose + // of mesh visualization. + state.step[istep].state = PrintStateBase::State::Invalidated; + else { + for (const ModelInstance* model_instance : print_object->model_object()->instances) { + // Only the instances, which are currently printable, will have the SLA support structures kept. + // The instances outside the print bed will have the GLVolumes of their support structures released. + if (model_instance->is_printable()) + aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); + } + } + } + } + sla_support_state.emplace_back(state); + } + } + std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. @@ -2020,6 +2060,118 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } + if (printer_technology == ptSLA) { + size_t idx = 0; + const SLAPrint *sla_print = this->sla_print(); + std::vector shift_zs(m_model->objects.size(), 0); + double relative_correction_z = sla_print->relative_correction().z(); + if (relative_correction_z <= EPSILON) + relative_correction_z = 1.; + for (const SLAPrintObject *print_object : sla_print->objects()) { + SLASupportState &state = sla_support_state[idx ++]; + const ModelObject *model_object = print_object->model_object(); + // Find an index of the ModelObject + int object_idx; + // There may be new SLA volumes added to the scene for this print_object. + // Find the object index of this print_object in the Model::objects list. + auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); + assert(it != sla_print->model().objects.end()); + object_idx = it - sla_print->model().objects.begin(); + // Cache the Z offset to be applied to all volumes with this object_idx. + shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z; + // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene. + // pairs of + std::vector> instances[std::tuple_size::value]; + for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) { + const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx]; + // Find index of ModelInstance corresponding to this SLAPrintObject::Instance. + auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), + [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; }); + assert(it != model_object->instances.end()); + int instance_idx = it - model_object->instances.begin(); + for (size_t istep = 0; istep < sla_steps.size(); ++istep) { + if (sla_steps[istep] == slaposDrillHoles) { + // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, + // not into its own GLVolume. + // There shall always be such a GLVolume allocated. + ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); + assert(!it->new_geometry()); + GLVolume& volume = *m_volumes.volumes[it->volume_idx]; + 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::State::Done) { + std::shared_ptr m = print_object->get_mesh_to_print(); + assert(m != nullptr && !m->empty()); + TriangleMesh mesh(*m); + // sla_trafo does not contain volume trafo. To get a mesh to create + // a new volume from, we have to apply vol trafo inverse separately. + const ModelObject& mo = *m_model->objects[volume.object_idx()]; + Transform3d trafo = sla_print->sla_trafo(mo) * mo.volumes.front()->get_transformation().get_matrix(); + mesh.transform(trafo.inverse()); +#if ENABLE_SMOOTH_NORMALS + volume.model.init_from(mesh, true); +#else + volume.model.init_from(mesh); + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_SMOOTH_NORMALS + } + else { + // Reload the original volume. +#if ENABLE_SMOOTH_NORMALS + volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#else + const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); + volume.model.init_from(new_mesh); + volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); +#endif // ENABLE_SMOOTH_NORMALS + } + } + //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable + // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables + // of various concenrs (model vs. 3D print path). + volume.offsets = { state.step[istep].timestamp }; + } + else if (state.step[istep].state == PrintStateBase::State::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); + assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); + if (it->new_geometry()) { + // This can be an SLA support structure that should not be rendered (in case someone used undo + // to revert to before it was generated). If that's the case, we should not generate anything. + if (model_object->sla_points_status != sla::PointsStatus::NoPoints) + instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); + else + shift_zs[object_idx] = 0.; + } + else { + // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. + m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); + } + } + } + } + + for (size_t istep = 0; istep < sla_steps.size(); ++istep) { + if (!instances[istep].empty()) + m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); + } + } + + // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed + for (GLVolume* volume : m_volumes.volumes) { + if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { + const SLAPrintObject* po = sla_print->objects()[volume->object_idx()]; + float zoffs = po->get_current_elevation() / sla_print->relative_correction().z(); + volume->set_sla_shift_z(zoffs); + } + } + } + if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); @@ -6805,9 +6957,9 @@ void GLCanvas3D::_load_sla_shells() // adds objects' volumes for (const SLAPrintObject* obj : print->objects()) { unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); - for (const SLAPrintObject::Instance& instance : obj->instances()) { - std::shared_ptr m = obj->get_mesh_to_print(); - if (m && !m->empty()) { + std::shared_ptr m = obj->get_mesh_to_print(); + if (m && !m->empty()) { + for (const SLAPrintObject::Instance& instance : obj->instances()) { add_volume(*obj, 0, instance, *m, GLVolume::MODEL_COLOR[0], true); // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // through the update_volumes_colors_by_extruder() call. @@ -6818,7 +6970,7 @@ void GLCanvas3D::_load_sla_shells() add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); } } - double shift_z = obj->get_current_elevation(); + const double shift_z = obj->get_current_elevation(); for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { // apply shift z m_volumes.volumes[i]->set_sla_shift_z(shift_z); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index d04a9cf2f..85e9d16ce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -101,6 +101,7 @@ void GLGizmoHollow::on_render() m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); + m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } @@ -772,6 +773,14 @@ RENDER_AGAIN: if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position_by_ratio(clp_dist, true); + // make sure supports are shown/hidden as appropriate + ImGui::Separator(); + bool show_sups = are_sla_supports_shown(); + if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) { + show_sla_supports(show_sups); + force_refresh = true; + } + m_imgui->disabled_end(); m_imgui->end(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp index 3ac438902..dac76b391 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp @@ -30,7 +30,8 @@ CommonGizmosDataID GLGizmoSlaBase::on_get_requirements() const int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::ObjectClipper)); + | int(CommonGizmosDataID::ObjectClipper) + | int(CommonGizmosDataID::SupportsClipper)); } void GLGizmoSlaBase::update_volumes() @@ -50,27 +51,54 @@ void GLGizmoSlaBase::update_volumes() TriangleMesh backend_mesh; std::shared_ptr preview_mesh_ptr = po->get_mesh_to_print(); - if (preview_mesh_ptr) - backend_mesh = TriangleMesh{*preview_mesh_ptr}; + if (preview_mesh_ptr != nullptr) + backend_mesh = TriangleMesh(*preview_mesh_ptr); if (!backend_mesh.empty()) { - // The backend has generated a valid mesh. Use it - backend_mesh.transform(po->trafo().inverse()); - m_volumes.volumes.emplace_back(new GLVolume()); - GLVolume* new_volume = m_volumes.volumes.back(); - new_volume->model.init_from(backend_mesh); - new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); - new_volume->set_sla_shift_z(po->get_current_elevation()); - new_volume->mesh_raycaster = std::make_unique(backend_mesh); auto last_comp_step = static_cast(po->last_completed_step()); if (last_comp_step == slaposCount) last_comp_step = -1; m_input_enabled = last_comp_step >= m_min_sla_print_object_step; - if (m_input_enabled) - new_volume->selected = true; // to set the proper color - else - new_volume->set_color(DISABLED_COLOR); + + const int object_idx = m_parent.get_selection().get_object_idx(); + const int instance_idx = m_parent.get_selection().get_instance_idx(); + const Geometry::Transformation& inst_trafo = po->model_object()->instances[instance_idx]->get_transformation(); + const double current_elevation = po->get_current_elevation(); + + auto add_volume = [this, object_idx, instance_idx, &inst_trafo, current_elevation](const TriangleMesh& mesh, int volume_id, bool add_mesh_raycaster = false) { + GLVolume* volume = m_volumes.volumes.emplace_back(new GLVolume()); + volume->model.init_from(mesh); + volume->set_instance_transformation(inst_trafo); + volume->set_sla_shift_z(current_elevation); + if (add_mesh_raycaster) + volume->mesh_raycaster = std::make_unique(mesh); + if (m_input_enabled) + volume->selected = true; // to set the proper color + else + volume->set_color(DISABLED_COLOR); + volume->composite_id = GLVolume::CompositeID(object_idx, volume_id, instance_idx); + }; + + const Transform3d po_trafo_inverse = po->trafo().inverse(); + + // main mesh + backend_mesh.transform(po_trafo_inverse); + add_volume(backend_mesh, 0, true); + + // supports mesh + TriangleMesh supports_mesh = po->support_mesh(); + if (!supports_mesh.empty()) { + supports_mesh.transform(po_trafo_inverse); + add_volume(supports_mesh, -int(slaposSupportTree)); + } + + // pad mesh + TriangleMesh pad_mesh = po->pad_mesh(); + if (!pad_mesh.empty()) { + pad_mesh.transform(po_trafo_inverse); + add_volume(pad_mesh, -int(slaposPad)); + } } if (m_volumes.volumes.empty()) { @@ -110,7 +138,11 @@ void GLGizmoSlaBase::render_volumes() clipping_plane.set_normal(-clipping_plane.get_normal()); m_volumes.set_clipping_plane(clipping_plane.get_data()); - m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); + for (GLVolume* v : m_volumes.volumes) { + v->is_active = m_show_sla_supports || (!v->is_sla_pad() && !v->is_sla_support()); + } + + m_volumes.render(GLVolumeCollection::ERenderType::Opaque, true, camera.get_view_matrix(), camera.get_projection_matrix()); shader->stop_using(); } @@ -119,7 +151,8 @@ void GLGizmoSlaBase::register_volume_raycasters_for_picking() { for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { const GLVolume* v = m_volumes.volumes[i]; - m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix())); + if (!v->is_sla_pad() && !v->is_sla_support()) + m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix())); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp index 438477598..acda5a8a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp @@ -40,11 +40,15 @@ protected: bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); + bool are_sla_supports_shown() const { return m_show_sla_supports; } + void show_sla_supports(bool show) { m_show_sla_supports = show; } + const GLVolumeCollection &volumes() const { return m_volumes; } private: GLVolumeCollection m_volumes; bool m_input_enabled{ false }; + bool m_show_sla_supports{ false }; int m_min_sla_print_object_step{ -1 }; std::vector> m_volume_raycasters; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 4d76dfb04..073d3fa29 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -25,8 +25,10 @@ namespace Slic3r { namespace GUI { GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles) -{} +: GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles) +{ + show_sla_supports(true); +} bool GLGizmoSlaSupports::on_init() { @@ -127,6 +129,7 @@ void GLGizmoSlaSupports::on_render() m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); + m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index a8dc16c55..1e5bd7955 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -453,83 +453,66 @@ void SupportsClipper::on_update() if (! mo || ! is_sla) return; - const GLCanvas3D* canvas = get_pool()->get_canvas(); - const PrintObjects& print_objects = canvas->sla_print()->objects(); - const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size())) - ? print_objects[m_print_object_idx] - : nullptr; + const SLAPrintObject* print_object = get_pool()->selection_info()->print_object(); + if (print_object == nullptr) + return; - // Find the respective SLAPrintObject. - if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { - m_print_objects_count = print_objects.size(); - m_print_object_idx = -1; - for (const SLAPrintObject* po : print_objects) { - ++m_print_object_idx; - if (po->model_object()->id() == mo->id()) { - print_object = po; - break; - } - } + if (print_object->get_mesh_to_print() == nullptr) { + // The object has been not sliced yet. We better dump the cached data. + m_supports_clipper.reset(); + m_pad_clipper.reset(); + return; } - if (print_object - && print_object->is_step_done(slaposSupportTree) - && ! print_object->support_mesh().empty()) - { - // If the supports are already calculated, save the timestamp of the respective step - // so we can later tell they were recalculated. - size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - if (! m_clipper || timestamp != m_old_timestamp) { - // The timestamp has changed. - m_clipper.reset(new MeshClipper); - // The mesh should already have the shared vertices calculated. - m_clipper->set_mesh(print_object->support_mesh().its); - m_old_timestamp = timestamp; - } + const TriangleMesh& support_mesh = print_object->support_mesh(); + if (support_mesh.empty()) { + // The supports are not available yet. We better dump the cached data. + m_supports_clipper.reset(); + } + else { + m_supports_clipper.reset(new MeshClipper); + m_supports_clipper->set_mesh(support_mesh.its); + } + + const TriangleMesh& pad_mesh = print_object->pad_mesh(); + if (pad_mesh.empty()) { + // The supports are not available yet. We better dump the cached data. + m_pad_clipper.reset(); + } + else { + m_pad_clipper.reset(new MeshClipper); + m_pad_clipper->set_mesh(pad_mesh.its); } - else - // The supports are not valid. We better dump the cached data. - m_clipper.reset(); } void SupportsClipper::on_release() { - m_clipper.reset(); - m_old_timestamp = 0; + m_supports_clipper.reset(); + m_pad_clipper.reset(); m_print_object_idx = -1; } void SupportsClipper::render_cut() const { const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper(); - if (ocl->get_position() == 0. - || ! m_clipper) + if (ocl->get_position() == 0.) return; const SelectionInfo* sel_info = get_pool()->selection_info(); - const ModelObject* mo = sel_info->model_object(); - const Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); - //Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation(); - Geometry::Transformation trafo = inst_trafo;// * vol_trafo; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + if (m_supports_clipper != nullptr) { + m_supports_clipper->set_plane(*ocl->get_clipping_plane()); + m_supports_clipper->set_transformation(inst_trafo); + m_supports_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f }); + } - // Get transformation of supports - Geometry::Transformation supports_trafo = trafo; - supports_trafo.set_scaling_factor(Vec3d::Ones()); - supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift())); - supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); - // I don't know why, but following seems to be correct. - supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), - 1, - 1.)); - - m_clipper->set_plane(*ocl->get_clipping_plane()); - m_clipper->set_transformation(supports_trafo); - - m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f }); - m_clipper->render_contour({ 1.f, 1.f, 1.f, 1.f }); + if (m_pad_clipper != nullptr) { + m_pad_clipper->set_plane(*ocl->get_clipping_plane()); + m_pad_clipper->set_transformation(inst_trafo); + m_pad_clipper->render_cut({ 0.6f, 0.f, 0.222f, 1.0f }); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 01b888be0..785c66076 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -293,10 +293,10 @@ protected: void on_release() override; private: - size_t m_old_timestamp = 0; int m_print_object_idx = -1; int m_print_objects_count = 0; - std::unique_ptr m_clipper; + std::unique_ptr m_supports_clipper; + std::unique_ptr m_pad_clipper; }; } // namespace CommonGizmosDataObjects From 201e9d493f1607c8e8162ab541f52fc2f207e797 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:32:21 +0100 Subject: [PATCH 105/201] Remove side effects from setter for enable picking. --- src/slic3r/GUI/GLCanvas3D.cpp | 1 - src/slic3r/GUI/GUI_Preview.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f95d748ba..e369e0f40 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1407,7 +1407,6 @@ void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_picking(bool enable) { m_picking_enabled = enable; - m_selection.set_mode(Selection::Instance); } void GLCanvas3D::enable_moving(bool enable) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d9dfc7db2..656bc0b53 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig m_canvas->allow_multisample(OpenGLManager::can_multisample()); m_canvas->enable_picking(true); + m_canvas->get_selection().set_mode(Selection::Instance); m_canvas->enable_moving(true); // XXX: more config from 3D.pm m_canvas->set_model(model); From d24472675c938986d0dc31ccc7edb6fa77e47cb0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:33:00 +0100 Subject: [PATCH 106/201] Keep color of object during drag over surface. --- src/slic3r/GUI/SurfaceDrag.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 4a48ced29..ecee0a4e0 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -80,6 +80,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // allow moving with object again canvas.enable_moving(true); + canvas.enable_picking(true); surface_drag.reset(); // only left up is correct @@ -167,6 +168,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // disable moving with object by mouse canvas.enable_moving(false); + canvas.enable_picking(false); return true; } From 7afabcde959640b90e376752a334495ce5d76dc6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 15:55:18 +0100 Subject: [PATCH 107/201] PlaceholderParser: Implemented one_of() matching function: 1st parameter is the text to match against, the rest of the parameters are pattern to be matched: either strings, then the match is exact, or regex enclosed in // or regex string starting with ~ For example one_of("a", "a", "b") finds a in "a", "b" one_of("abc", /.*a.*/) matches "abc" using regular expression /.*a.*/ --- src/libslic3r/PlaceholderParser.cpp | 62 +++++++++++++++++++-- tests/libslic3r/test_placeholder_parser.cpp | 12 ++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index cb0760d4d..5f8624ad3 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -587,7 +587,7 @@ namespace client param1.set_s(buf); } - static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) + static void regex_op(const expr &lhs, boost::iterator_range &rhs, char op, expr &out) { const std::string *subject = nullptr; if (lhs.type() == TYPE_STRING) { @@ -601,7 +601,7 @@ namespace client bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern)); if (op == '!') result = ! result; - lhs.set_b(result); + out.set_b(result); } catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) { // Syntax error in the regular expression boost::throw_exception(qi::expectation_failure( @@ -609,8 +609,37 @@ namespace client } } - static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '='); } - static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!'); } + static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '=', lhs); } + static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!', lhs); } + + static void one_of_test_init(expr &out) { + out.set_b(false); + } + template + static void one_of_test(const expr &match, const expr &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + if (pattern.type() != TYPE_STRING) + match.throw_exception("one_of(): Pattern has to be a string value"); + if (RegEx) { + try { + out.set_b(SLIC3R_REGEX_NAMESPACE::regex_match(match.s(), SLIC3R_REGEX_NAMESPACE::regex(pattern.s()))); + } catch (SLIC3R_REGEX_NAMESPACE::regex_error &) { + // Syntax error in the regular expression + pattern.throw_exception("Regular expression compilation failed"); + } + } else + out.set_b(match.s() == pattern.s()); + } + } + static void one_of_test_regex(const expr &match, boost::iterator_range &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + regex_op(match, pattern, '=', out); + } + } static void logical_op(expr &lhs, expr &rhs, char op) { @@ -1101,6 +1130,7 @@ namespace client { "multiplicative_expression", "Expecting an expression." }, { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, + { "one_of_list", "Expecting a list of string patterns (simple text or rexep)" }, { "variable_reference", "Expecting a variable reference."}, { "is_nil_test", "Expecting a scalar variable reference."}, { "variable", "Expecting a variable name."}, @@ -1221,6 +1251,7 @@ namespace client qi::_a_type _a; qi::_b_type _b; qi::_r1_type _r1; + qi::_r2_type _r2; // Starting symbol of the grammer. // The leading eps is required by the "expectation point" operator ">". @@ -1395,7 +1426,8 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] - | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1] + | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ] + | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1404,6 +1436,20 @@ namespace client ); unary_expression.name("unary_expression"); + one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2]; + one_of.name("one_of"); + one_of_list = + eps[px::bind(&expr::one_of_test_init, _val)] > + ( ',' > *( + ( + unary_expression(_r1)[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | (lit('~') > unary_expression(_r1))[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] + ) >> -lit(',')) + | eps + ); + one_of_list.name("one_of_list"); + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] @@ -1445,6 +1491,7 @@ namespace client ("random") ("round") ("not") + ("one_of") ("or") ("true"); @@ -1466,6 +1513,8 @@ namespace client debug(additive_expression); debug(multiplicative_expression); debug(unary_expression); + debug(one_of); + debug(one_of_list); debug(optional_parameter); debug(variable_reference); debug(variable); @@ -1517,6 +1566,9 @@ namespace client qi::rule(const MyContext*), spirit_encoding::space_type> variable; // Evaluating whether a nullable variable is nil. qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; + // Evaluating "one of" list of patterns. + qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> one_of; + qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 5248e089a..e40657d16 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -113,6 +113,18 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); } SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); } SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); } + SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); } SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); } SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); } From d653cc6ab5d6ec8c1c209444fef21c73e6d7bed7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 16:10:13 +0100 Subject: [PATCH 108/201] Fix crash made on MMU printers --- src/slic3r/Utils/RaycastManager.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 62722b076..f8f444241 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -313,11 +313,14 @@ namespace Slic3r::GUI{ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition) { - SceneRaycaster::EType type = SceneRaycaster::EType::Volume; - auto scene_casters = canvas.get_raycasters_for_picking(type); - const std::vector> &casters = *scene_casters; - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - const ModelObjectPtrs &objects = canvas.get_model()->objects; + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + if (scene_casters == nullptr) + return {}; + const std::vector> &casters = *scene_casters; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; RaycastManager::Meshes meshes; for (const std::shared_ptr &caster : casters) { @@ -327,9 +330,13 @@ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::A auto index_ = static_cast(index); if(index_ >= gl_volumes.size()) continue; - const GLVolume *gl_volume = gl_volumes[index_]; - const ModelVolume *volume = get_model_volume(*gl_volume, objects); - size_t id = volume->id().id; + const GLVolume *gl_volume = gl_volumes[index_]; + if (gl_volume == nullptr) + continue; + const ModelVolume *volume = get_model_volume(*gl_volume, objects); + if (volume == nullptr) + continue; + size_t id = volume->id().id; if (condition.skip(id)) continue; auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); From 9c70ed01e5f3345f7468c64add674dbf7ba93590 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 17:41:51 +0100 Subject: [PATCH 109/201] Fix add emboss volume on reflected --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 7 ++----- src/slic3r/Utils/RaycastManager.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index bc0b5e811..695399ab2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3462,15 +3462,12 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - // priv::reset_skew(hit_to_world); - Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); - // Create result volume transformation Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, priv::up_limit); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - // new transformation in world coor is surface_trmat - Transform3d volume_trmat = instance.inverse() * surface_trmat; + Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); + Transform3d volume_trmat = instance.inverse() * surface_trmat; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); return true; } diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index f8f444241..b2b758855 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -153,6 +153,8 @@ std::optional RaycastManager::first_hit(const Vec3d& point, for (int i = 0; i < 3; ++i) pts[i] = tr * hit_mesh->vertices(tri[i]).cast(); Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); + if (has_reflection(*hit_tramsformation)) + normal_world *= -1; normal_world.normalize(); SurfacePoint point_world{hit_world, normal_world}; @@ -298,8 +300,8 @@ RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &item template inline void erase(std::vector &vec, const std::vector &flags) { - assert(vec.size() == flags.size()); - if (flags.empty()) return; + if (vec.size() < flags.size() || flags.empty()) + return; // reverse iteration over flags to erase indices from back to front. for (int i = static_cast(flags.size()) - 1; i >= 0; --i) From ff5767b6f58e1464f5426536e1e4ce872fe5dc6e Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Mon, 20 Mar 2023 20:01:37 +0100 Subject: [PATCH 110/201] enable AABB tree over 3D lines --- src/libslic3r/AABBTreeLines.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 21678bfcd..2136e8edb 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -165,9 +165,9 @@ inline std::vector> get_intersections_with_line(si // Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies // during tree traversal. template -inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector &lines) +inline AABBTreeIndirect::Tree build_aabb_tree_over_indexed_lines(const std::vector &lines) { - using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>; + using TreeType = AABBTreeIndirect::Tree; // using CoordType = typename TreeType::CoordType; using VectorType = typename TreeType::VectorType; using BoundingBox = typename TreeType::BoundingBox; From ae016aceb9aee77749e7aa6d74525e7e47ec0ee7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Mar 2023 12:11:09 +0100 Subject: [PATCH 111/201] Fix for selection of unknodn font --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 224 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 3 +- 2 files changed, 91 insertions(+), 136 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 695399ab2..dd18cb878 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -53,7 +53,6 @@ #define SHOW_IMGUI_ATLAS #define SHOW_ICONS_TEXTURE #define SHOW_FINE_POSITION // draw convex hull around volume -#define SHOW_WX_WEIGHT_INPUT #define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position #define ALLOW_OPEN_NEAR_VOLUME #define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread @@ -531,6 +530,12 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) return; } + // Not known situation when could happend this is only for sure + if (!m_is_unknown_font && !m_style_manager.is_active_font()) + create_notification_not_valid_font("No active font in style. Select correct one."); + else if (!m_is_unknown_font && !m_style_manager.get_wx_font().IsOk()) + create_notification_not_valid_font("WxFont is not loaded properly."); + // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); @@ -1192,21 +1197,24 @@ void GLGizmoEmboss::draw_window() if (ImGui::Button("add svg")) choose_svg_file(); #endif // ALLOW_DEBUG_MODE - bool is_active_font = m_style_manager.is_active_font(); - if (!is_active_font) - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Warning: No font is selected. Select correct one.")); - + // Setter of indent must be befor disable !!! + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); + ScopeGuard indent_sc([](){ ImGui::PopStyleVar(/*ImGuiStyleVar_IndentSpacing*/); }); + // Disable all except selection of font, when open text from 3mf with unknown font m_imgui->disabled_begin(m_is_unknown_font); - ScopeGuard unknown_font_sc([&]() { - m_imgui->disabled_end(); - }); - draw_text_input(); - m_imgui->disabled_begin(!is_active_font); + ScopeGuard unknown_font_sc([imgui = m_imgui]() { imgui->disabled_end(/*m_is_unknown_font*/); }); + + draw_text_input(); - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); ImGui::Indent(); - draw_style_edit(); + // When unknown font is inside .3mf only font selection is allowed + m_imgui->disabled_end(/*m_is_unknown_font*/); + draw_font_list_line(); + m_imgui->disabled_begin(m_is_unknown_font); + bool use_inch = wxGetApp().app_config->get_bool("use_inches"); + draw_height(use_inch); + draw_depth(use_inch); ImGui::Unindent(); // close advanced style property when unknown font is selected @@ -1223,8 +1231,6 @@ void GLGizmoEmboss::draw_window() } else if (m_is_advanced_edit_style) set_minimal_window_size(false); - ImGui::PopStyleVar(); // ImGuiStyleVar_IndentSpacing - ImGui::Separator(); draw_style_list(); @@ -1234,8 +1240,6 @@ void GLGizmoEmboss::draw_window() ImGui::Separator(); draw_model_type(); } - - m_imgui->disabled_end(); // !is_active_font #ifdef SHOW_WX_FONT_DESCRIPTOR if (is_selected_style) @@ -1728,6 +1732,59 @@ bool GLGizmoEmboss::select_facename(const wxString &facename) return true; } +void GLGizmoEmboss::draw_font_list_line() +{ + bool exist_stored_style = m_style_manager.exist_stored_style(); + bool exist_change_in_font = m_style_manager.is_font_changed(); + const std::string& font_text = m_gui_cfg->translations.font; + if (exist_change_in_font || !exist_stored_style) + ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, font_text); + else + ImGuiWrapper::text(font_text); + + ImGui::SameLine(m_gui_cfg->input_offset); + + draw_font_list(); + + bool exist_change = false; + if (!m_is_unknown_font) { + ImGui::SameLine(); + if (draw_italic_button()) + exist_change = true; + ImGui::SameLine(); + if (draw_bold_button()) + exist_change = true; + } else { + // when exist unknown font add confirmation button + ImGui::SameLine(); + // Apply for actual selected font + if (ImGui::Button(_u8L("Apply").c_str())) + exist_change = true; + } + + EmbossStyle &style = m_style_manager.get_style(); + if (exist_change_in_font) { + ImGui::SameLine(ImGui::GetStyle().FramePadding.x); + if (draw_button(m_icons, IconType::undo)) { + const EmbossStyle *stored_style = m_style_manager.get_stored_style(); + + style.path = stored_style->path; + style.prop.boldness = stored_style->prop.boldness; + style.prop.skew = stored_style->prop.skew; + + wxFont new_wx_font = WxFontUtils::load_wxFont(style.path); + if (new_wx_font.IsOk() && m_style_manager.set_wx_font(new_wx_font)) + exist_change = true; + } else if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str()); + } + + if (exist_change) { + m_style_manager.clear_glyphs_cache(); + process(); + } +} + void GLGizmoEmboss::draw_font_list() { // Set partial @@ -1745,16 +1802,6 @@ void GLGizmoEmboss::draw_font_list() // When deletation of font appear this variable is set std::optional del_index; - // When is unknown font is inside .3mf only font selection is allowed - // Stop Imgui disable + Guard again start disabling - ScopeGuard unknown_font_sc; - if (m_is_unknown_font) { - m_imgui->disabled_end(); - unknown_font_sc.closure = [&]() { - m_imgui->disabled_begin(true); - }; - } - // Code const char *popup_id = "##font_list_popup"; const char *input_id = "##font_list_input"; @@ -1881,13 +1928,6 @@ void GLGizmoEmboss::draw_font_list() store(m_face_names); } - if (m_is_unknown_font) { - ImGui::SameLine(); - // Apply for actual selected font - if (ImGui::Button(_u8L("Apply").c_str())) - process(); - } - #ifdef ALLOW_ADD_FONT_BY_FILE ImGui::SameLine(); // select font file by file browser @@ -2580,89 +2620,6 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -void GLGizmoEmboss::draw_style_edit() -{ - { - // Check correct WxFont - const wxFont &wx_font = m_style_manager.get_wx_font(); - assert(wx_font.IsOk()); - if (!wx_font.IsOk()) { - ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); - return; - } - } - - bool exist_stored_style = m_style_manager.exist_stored_style(); - bool exist_change_in_font = m_style_manager.is_font_changed(); - const GuiCfg::Translations &tr = m_gui_cfg->translations; - if (exist_change_in_font || !exist_stored_style) - ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font); - else - ImGuiWrapper::text(tr.font); - ImGui::SameLine(m_gui_cfg->input_offset); - draw_font_list(); - bool exist_change = false; - if (!m_is_unknown_font) { - ImGui::SameLine(); - if (draw_italic_button()) - exist_change = true; - ImGui::SameLine(); - if (draw_bold_button()) - exist_change = true; - } - EmbossStyle &style = m_style_manager.get_style(); - if (exist_change_in_font) { - ImGui::SameLine(ImGui::GetStyle().FramePadding.x); - if (draw_button(m_icons, IconType::undo)) { - const EmbossStyle *stored_style = m_style_manager.get_stored_style(); - style.path = stored_style->path; - style.prop.boldness = stored_style->prop.boldness; - style.prop.skew = stored_style->prop.skew; - - wxFont new_wx_font = WxFontUtils::load_wxFont(style.path); - if (new_wx_font.IsOk() && - m_style_manager.set_wx_font(new_wx_font)) - exist_change = true; - } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str()); - } - - if (exist_change) { - m_style_manager.clear_glyphs_cache(); - process(); - } - - bool use_inch = wxGetApp().app_config->get_bool("use_inches"); - draw_height(use_inch); - draw_depth(use_inch); - -#ifdef SHOW_WX_WEIGHT_INPUT - if (wx_font.has_value()) { - ImGui::Text("%s", "weight"); - ImGui::SameLine(m_gui_cfg->input_offset); - ImGui::SetNextItemWidth(m_gui_cfg->input_width); - int weight = wx_font->GetNumericWeight(); - int min_weight = 1, max_weight = 1000; - if (ImGui::SliderInt("##weight", &weight, min_weight, max_weight)) { - wx_font->SetNumericWeight(weight); - m_style_manager.wx_font_changed(); - process(); - } - - wxFont f = wx_font->Bold(); - bool disable = f == *wx_font; - ImGui::SameLine(); - if (draw_button(IconType::bold, disable)) { - *wx_font = f; - m_style_manager.wx_font_changed(); - process(); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("wx Make bold").c_str()); - } -#endif // SHOW_WX_WEIGHT_INPUT -} - bool GLGizmoEmboss::set_height() { float &value = m_style_manager.get_style().prop.size_in_mm; @@ -3254,39 +3211,36 @@ bool GLGizmoEmboss::choose_svg_file() void GLGizmoEmboss::create_notification_not_valid_font( const TextConfiguration &tc) { - // not neccessary, but for sure that old notification doesnt exist - if (m_is_unknown_font) remove_notification_not_valid_font(); - m_is_unknown_font = true; - - auto type = NotificationType::UnknownFont; - auto level = - NotificationManager::NotificationLevel::WarningNotificationLevel; - 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 &face_name_3mf = tc.style.prop.face_name.value_or(tc.style.path); - const std::string &face_name_3mf = face_name_3mf_opt.has_value() ? - *face_name_3mf_opt : - tc.style.path; - - std::string face_name_by_wx; + std::optional face_name_by_wx; if (!face_name_opt.has_value()) { const wxFont& wx_font = m_style_manager.get_wx_font(); if (wx_font.IsOk()) { wxString wx_face_name = wx_font.GetFaceName(); - face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8()); + if (!wx_face_name.empty()) + face_name_by_wx = std::string(wx_face_name.ToUTF8().data()); } } - - const std::string &face_name = face_name_opt.has_value() ? *face_name_opt : - (!face_name_by_wx.empty() ? face_name_by_wx : es.path); - + const std::string &face_name = face_name_opt.value_or(face_name_by_wx.value_or(es.path)); std::string text = GUI::format(_L("Can't load exactly same font(\"%1%\"), " "Aplication selected a similar one(\"%2%\"). " "You have to specify font for enable edit text."), face_name_3mf, face_name); + create_notification_not_valid_font(text); +} + +void GLGizmoEmboss::create_notification_not_valid_font(const std::string &text) { + // not neccessary, but for sure that old notification doesnt exist + if (m_is_unknown_font) + remove_notification_not_valid_font(); + m_is_unknown_font = true; + + auto type = NotificationType::UnknownFont; + auto level = NotificationManager::NotificationLevel::WarningNotificationLevel; auto notification_manager = wxGetApp().plater()->get_notification_manager(); notification_manager->push_notification(type, level, text); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 730b7354c..ebbdf616c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -102,8 +102,8 @@ private: void init_font_name_texture(); struct FaceName; void draw_font_preview(FaceName &face, bool is_visible); + void draw_font_list_line(); void draw_font_list(); - void draw_style_edit(); void draw_height(bool use_inch); void draw_depth(bool use_inch); @@ -154,6 +154,7 @@ private: // When open text loaded from .3mf it could be written with unknown font bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); + void create_notification_not_valid_font(const std::string& text); void remove_notification_not_valid_font(); // This configs holds GUI layout size given by translated texts. From 4326929960e4fab5d6516e8fd9a0a73f26b4580d Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 21 Mar 2023 12:36:23 +0100 Subject: [PATCH 112/201] Updated output filename format. --- resources/profiles/PrusaResearch.idx | 1 + resources/profiles/PrusaResearch.ini | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 05970389c..77fdf5a83 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.0-alpha5 +1.9.0-alpha0 Updated output filename format. 1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. min_slic3r_version = 2.6.0-alpha1 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 3fb3aee3d..f57a8110d 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.7.0-alpha2 +config_version = 1.9.0-alpha0 # 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% @@ -192,7 +192,7 @@ notes = overhangs = 1 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode perimeters = 2 perimeter_extruder = 1 perimeter_extrusion_width = 0.45 @@ -390,7 +390,7 @@ support_material_xy_spacing = 80% support_material_interface_spacing = 0.2 support_material_spacing = 2.2 raft_first_layer_expansion = 2 -output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode infill_anchor = 2 infill_anchor_max = 15 thick_bridges = 0 From 9d156463b174b0b18b3aa2fe029bd9668fa306c6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Mar 2023 14:43:14 +0100 Subject: [PATCH 113/201] Fixed crash when rotating a part after slicing with sla printer --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ea79739c4..743b4504f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3766,7 +3766,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); #endif // ENABLE_WORLD_COORDINATE } - else if (selection_mode == Selection::Volume) { + else if (selection_mode == Selection::Volume && volume_idx >= 0) { #if ENABLE_WORLD_COORDINATE model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); #else From 37edec974cb85fe7f694d523f000adda86d8d61b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Mar 2023 15:33:27 +0100 Subject: [PATCH 114/201] Gizmo Hollow - Show clipped supports only when the supports are visible --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 85e9d16ce..89e0809fd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -101,7 +101,8 @@ void GLGizmoHollow::on_render() m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); - m_c->supports_clipper()->render_cut(); + if (are_sla_supports_shown()) + m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp index dac76b391..348fa5ca7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp @@ -144,7 +144,6 @@ void GLGizmoSlaBase::render_volumes() m_volumes.render(GLVolumeCollection::ERenderType::Opaque, true, camera.get_view_matrix(), camera.get_projection_matrix()); shader->stop_using(); - } void GLGizmoSlaBase::register_volume_raycasters_for_picking() From c251122e8993ed5aebc51ad1106cc790bc6dc8fd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 22 Mar 2023 09:41:30 +0100 Subject: [PATCH 115/201] Fixed SupportsClipper::render_cut() method --- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 1e5bd7955..debb22535 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -453,18 +453,18 @@ void SupportsClipper::on_update() if (! mo || ! is_sla) return; - const SLAPrintObject* print_object = get_pool()->selection_info()->print_object(); - if (print_object == nullptr) + const SLAPrintObject* po = get_pool()->selection_info()->print_object(); + if (po == nullptr) return; - if (print_object->get_mesh_to_print() == nullptr) { + if (po->get_mesh_to_print() == nullptr) { // The object has been not sliced yet. We better dump the cached data. m_supports_clipper.reset(); m_pad_clipper.reset(); return; } - const TriangleMesh& support_mesh = print_object->support_mesh(); + const TriangleMesh& support_mesh = po->support_mesh(); if (support_mesh.empty()) { // The supports are not available yet. We better dump the cached data. m_supports_clipper.reset(); @@ -474,7 +474,7 @@ void SupportsClipper::on_update() m_supports_clipper->set_mesh(support_mesh.its); } - const TriangleMesh& pad_mesh = print_object->pad_mesh(); + const TriangleMesh& pad_mesh = po->pad_mesh(); if (pad_mesh.empty()) { // The supports are not available yet. We better dump the cached data. m_pad_clipper.reset(); @@ -499,8 +499,16 @@ void SupportsClipper::render_cut() const if (ocl->get_position() == 0.) return; + const SLAPrintObject* po = get_pool()->selection_info()->print_object(); + if (po == nullptr) + return; + + Geometry::Transformation po_trafo(po->trafo()); + const SelectionInfo* sel_info = get_pool()->selection_info(); - const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + inst_trafo = Geometry::Transformation(inst_trafo.get_matrix() * po_trafo.get_matrix().inverse()); + inst_trafo.set_offset(inst_trafo.get_offset() + Vec3d(0.0, 0.0, sel_info->get_sla_shift())); if (m_supports_clipper != nullptr) { m_supports_clipper->set_plane(*ocl->get_clipping_plane()); From e1c289f51ae51e4fcdeae8c4a68090959497e17f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 22 Mar 2023 11:00:04 +0100 Subject: [PATCH 116/201] Fixed GLCanvas3D::reload_scene() method to avoid adding volumes coming from the sla backend --- src/slic3r/GUI/GLCanvas3D.cpp | 46 +---------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 743b4504f..a898d08fc 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2090,51 +2090,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != model_object->instances.end()); int instance_idx = it - model_object->instances.begin(); for (size_t istep = 0; istep < sla_steps.size(); ++istep) { - if (sla_steps[istep] == slaposDrillHoles) { - // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, - // not into its own GLVolume. - // There shall always be such a GLVolume allocated. - ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); - auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); - assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); - assert(!it->new_geometry()); - GLVolume& volume = *m_volumes.volumes[it->volume_idx]; - 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::State::Done) { - std::shared_ptr m = print_object->get_mesh_to_print(); - assert(m != nullptr && !m->empty()); - TriangleMesh mesh(*m); - // sla_trafo does not contain volume trafo. To get a mesh to create - // a new volume from, we have to apply vol trafo inverse separately. - const ModelObject& mo = *m_model->objects[volume.object_idx()]; - Transform3d trafo = sla_print->sla_trafo(mo) * mo.volumes.front()->get_transformation().get_matrix(); - mesh.transform(trafo.inverse()); -#if ENABLE_SMOOTH_NORMALS - volume.model.init_from(mesh, true); -#else - volume.model.init_from(mesh); - volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -#endif // ENABLE_SMOOTH_NORMALS - } - else { - // Reload the original volume. -#if ENABLE_SMOOTH_NORMALS - volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -#else - const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); - volume.model.init_from(new_mesh); - volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); -#endif // ENABLE_SMOOTH_NORMALS - } - } - //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable - // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables - // of various concenrs (model vs. 3D print path). - volume.offsets = { state.step[istep].timestamp }; - } - else if (state.step[istep].state == PrintStateBase::State::Done) { + if (state.step[istep].state == PrintStateBase::State::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 760f74ce6a2cb458f52fe693afd247b69a0e247e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Mar 2023 12:36:32 +0100 Subject: [PATCH 117/201] CutGizmo: Respect to the SLA shift --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e9b6fa694..5c18e9e96 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -876,6 +876,7 @@ void GLGizmoCut3D::on_set_state() } m_selected.clear(); m_parent.set_use_color_clip_plane(false); + m_c->selection_info()->set_use_shift(false); } } @@ -1239,7 +1240,10 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, { const Selection& selection = m_parent.get_selection(); - const Vec3d& instance_offset = selection.get_first_volume()->get_instance_offset(); + const auto first_volume = selection.get_first_volume(); + Vec3d instance_offset = first_volume->get_instance_offset(); + instance_offset[Z] += first_volume->get_sla_shift_z(); + const auto cut_matrix = Transform3d::Identity() * rotation_m.inverse() * translation_transform(instance_offset - plane_center); const Selection::IndicesList& idxs = selection.get_volume_idxs(); @@ -1357,6 +1361,12 @@ void GLGizmoCut3D::render_clipper_cut() void GLGizmoCut3D::on_render() { + if (m_state == On) { + // This gizmo is showing the object elevated. Tell the common + // SelectionInfo object to lie about the actual shift. + m_c->selection_info()->set_use_shift(true); + } + update_clipper(); init_picking_models(); From fbcc1ab27603a741246ef3762df67345cfed2c78 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Wed, 22 Mar 2023 13:23:07 +0100 Subject: [PATCH 118/201] Fix SPE-1595 - crash when no bridgin surfaces present and adaptive fill selected --- src/libslic3r/PrintObject.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 86cb16e2d..aa1216a0f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -528,7 +528,8 @@ std::pair PrintObject::prepare its_transform(mesh, to_octree * this->trafo_centered(), true); // Triangulate internal bridging surfaces. - std::vector> overhangs(surfaces_w_bottom_z.size()); + std::vector> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1))); + // ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally tbb::parallel_for(tbb::blocked_range(0, surfaces_w_bottom_z.size()), [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range &range) { for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { From 0a3db4525d91e402f01d651b9111610f6f79b238 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Mar 2023 14:50:53 +0100 Subject: [PATCH 119/201] Fix suggested by Stefan Csomor for releasing MacOs references. link: https://groups.google.com/g/wx-users/c/f2nVyT59H6M/m/_BocNmwCAgAJ --- src/slic3r/Utils/WxFontUtils.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index 1c191606d..2c862d5a5 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -1,6 +1,7 @@ #include "WxFontUtils.hpp" #include #include +#include "libslic3r/Utils.hpp" #if defined(__APPLE__) #include @@ -14,10 +15,9 @@ using namespace Slic3r; using namespace Slic3r::GUI; -namespace { - #ifdef __APPLE__ -static bool is_valid_ttf(std::string_view file_path) +namespace { +bool is_valid_ttf(std::string_view file_path) { if (file_path.empty()) return false; auto const pos_point = file_path.find_last_of('.'); @@ -34,8 +34,7 @@ static bool is_valid_ttf(std::string_view file_path) if (extension_size >= 5) return false; // a lot of symbols for extension if (extension_size <= 1) return false; // few letters for extension - std::string_view extension = file_path.substr(pos_point + 1, - extension_size); + std::string_view extension = file_path.substr(pos_point + 1, extension_size); // Because of MacOs - Courier, Geneva, Monaco if (extension == std::string_view("dfont")) return false; @@ -44,28 +43,29 @@ static bool is_valid_ttf(std::string_view file_path) } // get filepath from wxFont on Mac OsX -static std::string get_file_path(const wxFont& font) { +std::string get_file_path(const wxFont& font) { const wxNativeFontInfo *info = font.GetNativeFontInfo(); if (info == nullptr) return {}; CTFontDescriptorRef descriptor = info->GetCTFontDescriptor(); - CFURLRef typeref = (CFURLRef) - CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); + CFURLRef typeref = (CFURLRef)CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); if (typeref == NULL) return {}; + ScopeGuard sg([&typeref]() { CFRelease(typeref); }); CFStringRef url = CFURLGetString(typeref); if (url == NULL) return {}; - wxString file_uri; - wxCFTypeRef(url).GetValue(file_uri); + wxString file_uri(wxCFStringRef::AsString(url)); wxURI uri(file_uri); const wxString &path = uri.GetPath(); - std::string path_str(wxURI::Unescape(path).c_str()); + wxString path_unescaped = wxURI::Unescape(path); + std::string path_str = path_unescaped.ToUTF8().data(); BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ")."; return path_str; } -#endif // __APPLE__ } // namespace +#endif // __APPLE__ bool WxFontUtils::can_load(const wxFont &font) { + if (!font.IsOk()) return false; #ifdef _WIN32 return Emboss::can_load(font.GetHFONT()) != nullptr; From 963ca415d40f145ce2285de92b4f34401fad8096 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 21 Mar 2023 11:14:46 +0100 Subject: [PATCH 120/201] PlaceholderParser: new interpolate_table() "function" interpolate_table(x, (x0, y0), (x1, y1), (x2, y2), ...) interpolates a table at position x. --- src/libslic3r/PlaceholderParser.cpp | 116 +++++++++++++++++--- tests/libslic3r/test_placeholder_parser.cpp | 3 + 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 5f8624ad3..6ecb52233 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -249,6 +249,7 @@ namespace client TYPE_STRING, }; Type type() const { return m_type; } + bool numeric_type() const { return m_type == TYPE_INT || m_type == TYPE_DOUBLE; } bool& b() { return m_data.b; } bool b() const { return m_data.b; } @@ -472,8 +473,7 @@ namespace client static void compare_op(expr &lhs, expr &rhs, char op, bool invert) { bool value = false; - if ((lhs.type() == TYPE_INT || lhs.type() == TYPE_DOUBLE) && - (rhs.type() == TYPE_INT || rhs.type() == TYPE_DOUBLE)) { + if (lhs.numeric_type() && rhs.numeric_type()) { // Both types are numeric. switch (op) { case '=': @@ -681,7 +681,7 @@ namespace client void throw_if_not_numeric(const char *message) const { - if (this->type() != TYPE_INT && this->type() != TYPE_DOUBLE) + if (! this->numeric_type()) this->throw_exception(message); } @@ -964,6 +964,10 @@ namespace client { if (! opt.writable) ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + auto check_numeric = [](const expr ¶m) { + if (! param.numeric_type()) + param.throw_exception("Right side is not a numeric expression"); + }; if (opt.opt->is_vector()) { if (! opt.has_index()) ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); @@ -974,21 +978,18 @@ namespace client ctx->throw_exception("Index out of range", opt.it_range); switch (opt.opt->type()) { case coFloats: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); + check_numeric(param); static_cast(vec)->values[opt.index] = param.as_d(); break; case coInts: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); + check_numeric(param); static_cast(vec)->values[opt.index] = param.as_i(); break; case coStrings: static_cast(vec)->values[opt.index] = param.to_string(); break; case coPercents: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); + check_numeric(param); static_cast(vec)->values[opt.index] = param.as_d(); break; case coBools: @@ -1004,21 +1005,18 @@ namespace client ConfigOption *wropt = const_cast(opt.opt); switch (wropt->type()) { case coFloat: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); + check_numeric(param); static_cast(wropt)->value = param.as_d(); break; case coInt: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); + check_numeric(param); static_cast(wropt)->value = param.as_i(); break; case coString: static_cast(wropt)->value = param.to_string(); break; case coPercent: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); + check_numeric(param); static_cast(wropt)->value = param.as_d(); break; case coBool: @@ -1109,6 +1107,72 @@ namespace client } }; + template + struct InterpolateTableContext { + template + struct Item { + double x; + boost::iterator_range it_range_x; + double y; + }; + std::vector> table; + + static void init(const expr &x) { + if (!x.numeric_type()) + x.throw_exception("Interpolation value must be a number."); + } + static void add_pair(const expr &x, const expr &y, InterpolateTableContext &table) { + if (! x.numeric_type()) + x.throw_exception("X value of a table point must be a number."); + if (! y.numeric_type()) + y.throw_exception("Y value of a table point must be a number."); + table.table.push_back({ x.as_d(), x.it_range, y.as_d() }); + } + static void evaluate(const expr &expr_x, const InterpolateTableContext &table, expr &out) { + // Check whether the table X values are sorted. + double x = expr_x.as_d(); + bool evaluated = false; + for (size_t i = 1; i < table.table.size(); ++i) { + double x0 = table.table[i - 1].x; + double x1 = table.table[i].x; + if (x0 > x1) + boost::throw_exception(qi::expectation_failure( + table.table[i - 1].it_range_x.begin(), table.table[i].it_range_x.end(), spirit::info("X coordinates of the table must be increasing"))); + if (! evaluated && x >= x0 && x <= x1) { + double y0 = table.table[i - 1].y; + double y1 = table.table[i].y; + if (x == x0) + out.set_d(y0); + else if (x == x1) + out.set_d(y1); + else if (is_approx(x0, x1)) + out.set_d(0.5 * (y0 + y1)); + else + out.set_d(Slic3r::lerp(y0, y1, (x - x0) / (x1 - x0))); + evaluated = true; + } + } + if (! evaluated) { + // Clamp x into the table range with EPSILON. + if (x > table.table.front().x - EPSILON) + out.set_d(table.table.front().y); + else if (x < table.table.back().x + EPSILON) + out.set_d(table.table.back().y); + else + // The value is really outside the table range. + expr_x.throw_exception("Interpolation value is outside the table range"); + } + } + }; + + template + std::ostream& operator<<(std::ostream &os, const InterpolateTableContext &table_context) + { + for (const auto &item : table_context.table) + os << "(" << item.x << "," << item.y << ")"; + return os; + } + // Table to translate symbol tag to a human readable error message. std::map MyContext::tag_to_error_message = { { "eoi", "Unknown syntax error" }, @@ -1428,6 +1492,7 @@ namespace client | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ] | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] + | (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1440,16 +1505,26 @@ namespace client one_of.name("one_of"); one_of_list = eps[px::bind(&expr::one_of_test_init, _val)] > - ( ',' > *( + ( ( ',' > *( ( unary_expression(_r1)[px::bind(&expr::template one_of_test, _r2, _1, _val)] | (lit('~') > unary_expression(_r1))[px::bind(&expr::template one_of_test, _r2, _1, _val)] | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] ) >> -lit(',')) - | eps + ) + | eps ); one_of_list.name("one_of_list"); + interpolate_table = (unary_expression(_r1)[_a = _1] > ',' > interpolate_table_list(_r1, _a)) + [px::bind(&InterpolateTableContext::evaluate, _a, _2, _val)]; + interpolate_table.name("interpolate_table"); + interpolate_table_list = + eps[px::bind(&InterpolateTableContext::init, _r2)] > + ( *(( lit('(') > unary_expression(_r1) > ',' > unary_expression(_r1) > ')' ) + [px::bind(&InterpolateTableContext::add_pair, _1, _2, _val)] >> -lit(',')) ); + interpolate_table.name("interpolate_table_list"); + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] @@ -1486,6 +1561,7 @@ namespace client ("elsif") ("endif") ("false") + ("interpolate_table") ("min") ("max") ("random") @@ -1501,9 +1577,12 @@ namespace client debug(text_block); debug(macro); debug(if_else_output); + debug(interpolate_table); // debug(switch_output); debug(legacy_variable_expansion); debug(identifier); + debug(interpolate_table); + debug(interpolate_table_list); debug(conditional_expression); debug(logical_or_expression); debug(logical_and_expression); @@ -1569,6 +1648,9 @@ namespace client // Evaluating "one of" list of patterns. qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> one_of; qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list; + // Evaluating the "interpolate_table" expression. + qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> interpolate_table; + qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> interpolate_table_list; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index e40657d16..320b004ae 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -71,6 +71,9 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); } SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); } SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); } + SECTION("math: interpolate_table(13.84375892476, (0, 0), (20, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13.84375892476, (0, 0), (20, 20))}")) == Approx(13.84375892476)); } + SECTION("math: interpolate_table(13, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13, (0, 0), (20, 20), (30, 20))}")) == Approx(13.)); } + SECTION("math: interpolate_table(25, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(25, (0, 0), (20, 20), (30, 20))}")) == Approx(20.)); } // Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions. // first_layer_extrusion_width ratio_over first_layer_heigth. From c28585ab7fa62a56a1f188284b8bf782b734003f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 22 Mar 2023 17:46:47 +0100 Subject: [PATCH 121/201] WIP PlaceholderParser: Support for local and global variables. Implements #4048 #7196 Syntax: (global|local) variable_name = (scalar_expression|vector_variable|array_expr|initializer_list) array_expr := array(repeat, value) initializer_list := (value, value, value, ...) The type of the newly created variable is defined by the type of the right hand side intitializer. Newly declared variable must not override an existing variable. Variable may be assigned with global|local expression, but its type must not be changed. Newly the assignment operator also accepts the same right hand expressions as the global|local variable definition. --- src/libslic3r/GCode.cpp | 2 + src/libslic3r/PlaceholderParser.cpp | 538 +++++++++++++++++++- src/libslic3r/PlaceholderParser.hpp | 5 +- tests/libslic3r/test_placeholder_parser.cpp | 77 +++ 4 files changed, 602 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e841a5c43..65549d2e8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1086,6 +1086,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + // Enable passing global variables between PlaceholderParser invocations. + m_placeholder_parser_context.global_config = std::make_unique(); print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode"); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 6ecb52233..78ded40c4 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -191,6 +191,10 @@ namespace client struct expr { expr() {} + expr(const expr &rhs) : m_type(rhs.type()), it_range(rhs.it_range) + { if (rhs.type() == TYPE_STRING) m_data.s = new std::string(*rhs.m_data.s); else m_data.set(rhs.m_data); } + expr(expr &&rhs) : expr(std::move(rhs), rhs.it_range.begin(), rhs.it_range.end()) {} + explicit expr(bool b) : m_type(TYPE_BOOL) { m_data.b = b; } explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_BOOL), it_range(it_begin, it_end) { m_data.b = b; } explicit expr(int i) : m_type(TYPE_INT) { m_data.i = i; } @@ -201,11 +205,8 @@ namespace client explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); } - expr(const expr &rhs) : m_type(rhs.type()), it_range(rhs.it_range) - { if (rhs.type() == TYPE_STRING) m_data.s = new std::string(*rhs.m_data.s); else m_data.set(rhs.m_data); } - explicit expr(expr &&rhs) : expr(rhs, rhs.it_range.begin(), rhs.it_range.end()) {} explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end } - { + { m_data.set(rhs.m_data); rhs.m_type = TYPE_EMPTY; } @@ -723,7 +724,10 @@ namespace client const DynamicConfig *config = nullptr; const DynamicConfig *config_override = nullptr; mutable DynamicConfig *config_outputs = nullptr; + // Local variables, read / write + mutable DynamicConfig config_local; size_t current_extruder_id = 0; + // Random number generator and optionally global variables. PlaceholderParser::ContextData *context_data = nullptr; // If false, the macro_processor will evaluate a full macro. // If true, the macro processor will evaluate just a boolean condition using the full expressive power of the macro processor. @@ -748,7 +752,24 @@ namespace client } const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } - ConfigOption* resolve_output_symbol(const std::string &opt_key) const { return this->config_outputs ? this->config_outputs->optptr(opt_key, false) : nullptr; } + ConfigOption* resolve_output_symbol(const std::string &opt_key) const { + ConfigOption *out = nullptr; + if (this->config_outputs) + out = this->config_outputs->optptr(opt_key, false); + if (out == nullptr && this->context_data != nullptr && this->context_data->global_config) + out = this->context_data->global_config->optptr(opt_key); + if (out == nullptr) + out = this->config_local.optptr(opt_key); + return out; + } + void store_new_variable(const std::string &opt_key, ConfigOption *opt, bool global_variable) { + assert(opt != nullptr); + if (global_variable) { + assert(this->context_data != nullptr && this->context_data->global_config); + this->context_data->global_config->set_key_value(opt_key, opt); + } else + this->config_local.set_key_value(opt_key ,opt); + } template static void legacy_variable_expansion( @@ -953,17 +974,101 @@ namespace client output.it_range = opt.it_range; } - // Decoding a scalar variable symbol "opt", assigning it a value of "param". + template + struct NewOldVariable { + std::string name; + boost::iterator_range it_range; + ConfigOption *opt{ nullptr }; + }; template - static void variable_assign( - const MyContext *ctx, - OptWithPos &opt, - expr ¶m, - // Not used, just clear it. - std::string &out) + static void new_old_variable( + const MyContext *ctx, + bool global_variable, + const boost::iterator_range &it_range, + NewOldVariable &out) { + t_config_option_key key(std::string(it_range.begin(), it_range.end())); + if (const ConfigOption* opt = ctx->resolve_symbol(key); opt) + ctx->throw_exception("Symbol is already defined in read-only system dictionary", it_range); + if (ctx->config_outputs && ctx->config_outputs->optptr(key)) + ctx->throw_exception("Symbol is already defined as system output variable", it_range); + + bool has_global_dictionary = ctx->context_data != nullptr && ctx->context_data->global_config; + if (global_variable) { + if (! has_global_dictionary) + ctx->throw_exception("Global variables are not available in this context", it_range); + if (ctx->config_local.optptr(key)) + ctx->throw_exception("Variable name already defined in local scope", it_range); + out.opt = ctx->context_data->global_config->optptr(key); + } else { + if (has_global_dictionary && ctx->context_data->global_config->optptr(key)) + ctx->throw_exception("Variable name already defined in global scope", it_range); + out.opt = ctx->config_local.optptr(key); + } + + out.name = std::move(key); + out.it_range = it_range; + } + + template + static void new_scalar_variable( + const MyContext *ctx, + bool global_variable, + NewOldVariable &output_variable, + const expr ¶m) + { + auto check_numeric = [](const expr ¶m) { + if (! param.numeric_type()) + param.throw_exception("Right side is not a numeric expression"); + }; + if (output_variable.opt) { + if (output_variable.opt->is_vector()) + param.throw_exception("Cannot assign a scalar value to a vector variable."); + switch (output_variable.opt->type()) { + case coFloat: + check_numeric(param); + static_cast(output_variable.opt)->value = param.as_d(); + break; + case coInt: + check_numeric(param); + static_cast(output_variable.opt)->value = param.as_i(); + break; + case coString: + static_cast(output_variable.opt)->value = param.to_string(); + break; + case coBool: + if (param.type() != expr::TYPE_BOOL) + param.throw_exception("Right side is not a boolean expression"); + static_cast(output_variable.opt)->value = param.b(); + break; + default: assert(false); + } + } else { + switch (param.type()) { + case expr::TYPE_BOOL: output_variable.opt = new ConfigOptionBool(param.b()); break; + case expr::TYPE_INT: output_variable.opt = new ConfigOptionInt(param.i()); break; + case expr::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloat(param.d()); break; + case expr::TYPE_STRING: output_variable.opt = new ConfigOptionString(param.s()); break; + default: assert(false); + } + const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); + } + } + + template + static void check_writable(const MyContext *ctx, OptWithPos &opt) { if (! opt.writable) ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + } + + // Decoding a scalar variable symbol "opt", assigning it a value of "param". + template + static void assign_scalar_variable( + const MyContext *ctx, + OptWithPos &opt, + expr ¶m) + { + check_writable(ctx, opt); auto check_numeric = [](const expr ¶m) { if (! param.numeric_type()) param.throw_exception("Right side is not a numeric expression"); @@ -1028,7 +1133,363 @@ namespace client ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); } } - out.clear(); + } + + template + static void new_vector_variable_array( + const MyContext *ctx, + bool global_variable, + NewOldVariable &output_variable, + const expr &expr_count, + const expr &expr_value) + { + auto check_numeric = [](const expr ¶m) { + if (! param.numeric_type()) + param.throw_exception("Right side is not a numeric expression"); + }; + auto evaluate_count = [](const expr &expr_count) -> size_t { + if (expr_count.type() != expr::TYPE_INT) + expr_count.throw_exception("Expected number of elements to fill a vector with."); + int count = expr_count.i(); + if (count < 0) + expr_count.throw_exception("Negative number of elements specified."); + return size_t(count); + }; + if (output_variable.opt) { + if (output_variable.opt->is_scalar()) + expr_value.throw_exception("Cannot assign a vector value to a scalar variable."); + size_t count = evaluate_count(expr_count); + switch (output_variable.opt->type()) { + case coFloats: + check_numeric(expr_value); + static_cast(output_variable.opt)->values.assign(count, expr_value.as_d()); + break; + case coInts: + check_numeric(expr_value); + static_cast(output_variable.opt)->values.assign(count, expr_value.as_i()); + break; + case coStrings: + static_cast(output_variable.opt)->values.assign(count, expr_value.to_string()); + break; + case coBools: + if (expr_value.type() != expr::TYPE_BOOL) + expr_value.throw_exception("Right side is not a boolean expression"); + static_cast(output_variable.opt)->values.assign(count, expr_value.b()); + break; + default: assert(false); + } + } else { + size_t count = evaluate_count(expr_count); + switch (expr_value.type()) { + case expr::TYPE_BOOL: output_variable.opt = new ConfigOptionBools(count, expr_value.b()); break; + case expr::TYPE_INT: output_variable.opt = new ConfigOptionInts(count, expr_value.i()); break; + case expr::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloats(count, expr_value.d()); break; + case expr::TYPE_STRING: output_variable.opt = new ConfigOptionStrings(count, expr_value.s()); break; + default: assert(false); + } + const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); + } + } + + template + static void assign_vector_variable_array( + const MyContext *ctx, + OptWithPos &lhs, + const expr &expr_count, + const expr &expr_value) + { + check_writable(ctx, lhs); + auto check_numeric = [](const expr ¶m) { + if (! param.numeric_type()) + param.throw_exception("Right side is not a numeric expression"); + }; + auto evaluate_count = [](const expr &expr_count) -> size_t { + if (expr_count.type() != expr::TYPE_INT) + expr_count.throw_exception("Expected number of elements to fill a vector with."); + int count = expr_count.i(); + if (count < 0) + expr_count.throw_exception("Negative number of elements specified."); + return size_t(count); + }; + if (lhs.opt->is_scalar()) + expr_value.throw_exception("Cannot assign a vector value to a scalar variable."); + auto *opt = const_cast(lhs.opt); + size_t count = evaluate_count(expr_count); + switch (lhs.opt->type()) { + case coFloats: + check_numeric(expr_value); + static_cast(opt)->values.assign(count, expr_value.as_d()); + break; + case coInts: + check_numeric(expr_value); + static_cast(opt)->values.assign(count, expr_value.as_i()); + break; + case coStrings: + static_cast(opt)->values.assign(count, expr_value.to_string()); + break; + case coBools: + if (expr_value.type() != expr::TYPE_BOOL) + expr_value.throw_exception("Right side is not a boolean expression"); + static_cast(opt)->values.assign(count, expr_value.b()); + break; + default: assert(false); + } + } + + template + static void new_vector_variable_initializer_list( + const MyContext *ctx, + bool global_variable, + NewOldVariable &output_variable, + const std::vector> &il) + { + if (! output_variable.opt) { + // First guesstimate type of the output vector. + size_t num_bool = 0; + size_t num_int = 0; + size_t num_double = 0; + size_t num_string = 0; + for (auto &i : il) + switch (i.type()) { + case expr::TYPE_BOOL: ++ num_bool; break; + case expr::TYPE_INT: ++ num_int; break; + case expr::TYPE_DOUBLE: ++ num_double; break; + case expr::TYPE_STRING: ++ num_string; break; + default: assert(false); + } + if (num_string > 0) + // Convert everything to strings. + output_variable.opt = new ConfigOptionStrings(); + else if (num_bool > 0) { + if (num_double + num_int > 0) + ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range{ il.front().it_range.begin(), il.back().it_range.end() }); + output_variable.opt = new ConfigOptionBools(); + } else + // Output is numeric. + output_variable.opt = num_double == 0 ? static_cast(new ConfigOptionInts()) : static_cast(new ConfigOptionFloats()); + const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); + } + + auto check_numeric = [](const std::vector> &il) { + for (auto& i : il) + if (!i.numeric_type()) + i.throw_exception("Right side is not a numeric expression"); + }; + + if (output_variable.opt->is_scalar()) + ctx->throw_exception("Cannot assign a vector value to a scalar variable.", output_variable.it_range); + + switch (output_variable.opt->type()) { + case coFloats: + { + check_numeric(il); + auto &out = static_cast(output_variable.opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto &i : il) + out.emplace_back(i.as_d()); + break; + } + case coInts: + { + check_numeric(il); + auto &out = static_cast(output_variable.opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto& i : il) + out.emplace_back(i.as_i()); + break; + } + case coStrings: + { + auto &out = static_cast(output_variable.opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto &i : il) + out.emplace_back(i.to_string()); + break; + } + case coBools: + { + auto &out = static_cast(output_variable.opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto &i : il) + if (i.type() == expr::TYPE_BOOL) + out.emplace_back(i.b()); + else + i.throw_exception("Right side is not a boolean expression"); + break; + } + default: + assert(false); + } + } + + template + static void assign_vector_variable_initializer_list( + const MyContext *ctx, + OptWithPos &lhs, + const std::vector> &il) + { + check_writable(ctx, lhs); + auto check_numeric = [](const std::vector> &il) { + for (auto &i : il) + if (! i.numeric_type()) + i.throw_exception("Right side is not a numeric expression"); + }; + + if (lhs.opt->is_scalar()) + ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); + + ConfigOption *opt = const_cast(lhs.opt); + switch (lhs.opt->type()) { + case coFloats: + { + check_numeric(il); + auto &out = static_cast(opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto &i : il) + out.emplace_back(i.as_d()); + break; + } + case coInts: + { + check_numeric(il); + auto &out = static_cast(opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto& i : il) + out.emplace_back(i.as_i()); + break; + } + case coStrings: + { + auto &out = static_cast(opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto &i : il) + out.emplace_back(i.to_string()); + break; + } + case coBools: + { + auto &out = static_cast(opt)->values; + out.clear(); + out.reserve(il.size()); + for (auto &i : il) + if (i.type() == expr::TYPE_BOOL) + out.emplace_back(i.b()); + else + i.throw_exception("Right side is not a boolean expression"); + break; + } + default: + assert(false); + } + } + + template + static bool new_vector_variable_copy( + const MyContext *ctx, + bool global_variable, + NewOldVariable &output_variable, + const OptWithPos &src_variable) + { + if (! is_vector_variable_reference(src_variable)) + // Skip parsing this branch, bactrack. + return false; + + if (! output_variable.opt) { + if (one_of(src_variable.opt->type(), { coFloats, coInts, coStrings, coBools })) + output_variable.opt = src_variable.opt->clone(); + else if (src_variable.opt->type() == coPercents) + output_variable.opt = new ConfigOptionFloats(static_cast(src_variable.opt)->values); + else + ctx->throw_exception("Duplicating this vector variable is not supported", src_variable.it_range); + const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); + } + + switch (output_variable.opt->type()) { + case coFloats: + if (output_variable.opt->type() != coFloats) + ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); + static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; + break; + case coInts: + if (output_variable.opt->type() != coInts) + ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); + static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; + break; + case coStrings: + if (output_variable.opt->type() != coStrings) + ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); + static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; + break; + case coBools: + if (output_variable.opt->type() != coBools) + ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); + static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; + break; + default: + assert(false); + } + // Continue parsing. + return true; + } + + template + static bool is_vector_variable_reference(const OptWithPos &var) { + return ! var.has_index() && var.opt->is_vector(); + } + + template + static bool assign_vector_variable_copy( + const MyContext *ctx, + OptWithPos &lhs, + const OptWithPos &src_variable) + { + if (! is_vector_variable_reference(src_variable)) + // Skip parsing this branch, bactrack. + return false; + + check_writable(ctx, lhs); + + auto *opt = const_cast(lhs.opt); + switch (lhs.opt->type()) { + case coFloats: + if (lhs.opt->type() != coFloats) + ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range); + static_cast(opt)->values = static_cast(src_variable.opt)->values; + break; + case coInts: + if (lhs.opt->type() != coInts) + ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range); + static_cast(opt)->values = static_cast(src_variable.opt)->values; + break; + case coStrings: + if (lhs.opt->type() != coStrings) + ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range); + static_cast(opt)->values = static_cast(src_variable.opt)->values; + break; + case coBools: + if (lhs.opt->type() != coBools) + ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range); + static_cast(opt)->values = static_cast(src_variable.opt)->values; + break; + default: + assert(false); + } + + // Continue parsing. + return true; + } + + template + static void new_vector_variable_initializer_list_append(std::vector> &list, expr &expr) + { + list.emplace_back(std::move(expr)); } // Verify that the expression returns an integer, which may be used @@ -1175,6 +1636,7 @@ namespace client // Table to translate symbol tag to a human readable error message. std::map MyContext::tag_to_error_message = { + { "array", "Unknown syntax error" }, { "eoi", "Unknown syntax error" }, { "start", "Unknown syntax error" }, { "text", "Invalid text." }, @@ -1335,7 +1797,7 @@ namespace client // Allow back tracking after '{' in case of a text_block embedded inside a condition. // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. - | (lit('{') >> macro(_r1) [_val+=_1] > '}') + | (lit('{') >> macro(_r1)[_val+=_1] > *(+lit(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -1351,6 +1813,7 @@ namespace client (kw["if"] > if_else_output(_r1) [_val = _1]) // | (kw["switch"] > switch_output(_r1) [_val = _1]) | (assignment_statement(_r1) [_val = _1]) + | (new_variable_statement(_r1) [_val = _1]) | (additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) ; macro.name("macro"); @@ -1445,8 +1908,39 @@ namespace client multiplicative_expression.name("multiplicative_expression"); assignment_statement = - (variable_reference(_r1) >> '=' > additive_expression(_r1)) - [px::bind(&MyContext::variable_assign, _r1, _1, _2, _val)]; + variable_reference(_r1)[_a = _1] >> '=' > + ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. + (lit('(') > new_variable_initializer_list(_r1) > ')') + [px::bind(&MyContext::assign_vector_variable_initializer_list, _r1, _a, _1)] + // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. + // Only process such variable references, which return a naked vector variable. + | variable_reference(_r1) + [px::ref(qi::_pass) = px::bind(&MyContext::assign_vector_variable_copy, _r1, _a, _1)] + // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. + | conditional_expression(_r1) + [px::bind(&MyContext::assign_scalar_variable, _r1, _a, _1)] + | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") + [px::bind(&MyContext::assign_vector_variable_array, _r1, _a, _1, _2)] + ); + + new_variable_statement = + (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable, _r1, _a, _1, _b)] > lit('=') > + ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. + (lit('(') > new_variable_initializer_list(_r1) > ')') + [px::bind(&MyContext::new_vector_variable_initializer_list, _r1, _a, _b, _1)] + // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. + // Only process such variable references, which return a naked vector variable. + | variable_reference(_r1) + [px::ref(qi::_pass) = px::bind(&MyContext::new_vector_variable_copy, _r1, _a, _b, _1)] + // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. + | conditional_expression(_r1) + [px::bind(&MyContext::new_scalar_variable, _r1, _a, _b, _1)] + | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") + [px::bind(&MyContext::new_vector_variable_array, _r1, _a, _b, _1, _2)] + ); + new_variable_initializer_list = + conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append, _val, _1)] >> + *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append, _val, _1)]); struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) @@ -1544,23 +2038,26 @@ namespace client variable_reference.name("variable reference"); variable = identifier[ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; - variable.name("variable reference"); + variable.name("variable name"); regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']]; regular_expression.name("regular_expression"); keywords.add ("and") + ("array") ("digits") ("zdigits") ("if") ("int") ("is_nil") + ("local") //("inf") ("else") ("elsif") ("endif") ("false") + ("global") ("interpolate_table") ("min") ("max") @@ -1653,9 +2150,12 @@ namespace client qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> interpolate_table_list; qi::rule, spirit_encoding::space_type> if_else_output; - qi::rule, int>, spirit_encoding::space_type> assignment_statement; -// qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; + qi::rule>, spirit_encoding::space_type> assignment_statement; + // Allocating new local or global variables. + qi::rule>, spirit_encoding::space_type> new_variable_statement; + qi::rule>(const MyContext*), spirit_encoding::space_type> new_variable_initializer_list; +// qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; qi::symbols keywords; }; } diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index a3f051558..39c33206c 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -20,7 +20,10 @@ public: // In the future, the context may hold variables created and modified by the PlaceholderParser // and shared between the PlaceholderParser::process() invocations. struct ContextData { - std::mt19937 rng; + std::mt19937 rng; + // If defined, then this dictionary is used by the scripts to define user variables and persist them + // between PlaceholderParser evaluations. + std::unique_ptr global_config; }; PlaceholderParser(const DynamicConfig *external_config = nullptr); diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 320b004ae..ee1461baf 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -39,6 +39,10 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("nullable is not null") { REQUIRE(parser.process("{is_nil(filament_retract_length[0])}") == "false"); } SECTION("nullable is null") { REQUIRE(parser.process("{is_nil(filament_retract_length[1])}") == "true"); } SECTION("nullable is not null 2") { REQUIRE(parser.process("{is_nil(filament_retract_length[2])}") == "false"); } + SECTION("multiple expressions") { REQUIRE(parser.process("{temperature[foo];temperature[foo]}") == "357357"); } + SECTION("multiple expressions with semicolons") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];}") == "357357"); } + SECTION("multiple expressions with semicolons 2") { REQUIRE(parser.process("{temperature[foo];;temperature[foo];}") == "357357"); } + SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); } // Test the math expressions. SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); } @@ -146,3 +150,76 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.)); } } + +SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { + PlaceholderParser parser; + auto config = DynamicPrintConfig::full_print_config(); + + config.set_deserialize_strict({ + { "filament_notes", "testnotes" }, + { "enable_dynamic_fan_speeds", "1" }, + { "nozzle_diameter", "0.6;0.6;0.6;0.6" }, + { "temperature", "357;359;363;378" } + }); + + PlaceholderParser::ContextData context_with_global_dict; + context_with_global_dict.global_config = std::make_unique(); + + SECTION("create an int local variable") { REQUIRE(parser.process("{local myint = 33+2}{myint}", 0, nullptr, nullptr, nullptr) == "35"); } + SECTION("create a string local variable") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); } + SECTION("create a bool local variable") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, nullptr) == "true"); } + SECTION("create an int global variable") { REQUIRE(parser.process("{global myint = 33+2}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "35"); } + SECTION("create a string global variable") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); } + SECTION("create a bool global variable") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); } + + SECTION("create an int local variable and overwrite it") { REQUIRE(parser.process("{local myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); } + SECTION("create a string local variable and overwrite it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); } + SECTION("create a bool local variable and overwrite it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); } + SECTION("create an int global variable and overwrite it") { REQUIRE(parser.process("{global myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); } + SECTION("create a string global variable and overwrite it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); } + SECTION("create a bool global variable and overwrite it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); } + + SECTION("create an int local variable and redefine it") { REQUIRE(parser.process("{local myint = 33+2}{local myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); } + SECTION("create a string local variable and redefine it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{local mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); } + SECTION("create a bool local variable and redefine it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{local mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); } + SECTION("create an int global variable and redefine it") { REQUIRE(parser.process("{global myint = 33+2}{global myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); } + SECTION("create a string global variable and redefine it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{global mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); } + SECTION("create a bool global variable and redefine it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{global mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); } + + SECTION("create an ints local variable with array()") { REQUIRE(parser.process("{local myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); } + SECTION("create a strings local variable array()") { REQUIRE(parser.process("{local mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); } + SECTION("create a bools local variable array()") { REQUIRE(parser.process("{local mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); } + SECTION("create an ints global variable array()") { REQUIRE(parser.process("{global myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); } + SECTION("create a strings global variable array()") { REQUIRE(parser.process("{global mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); } + SECTION("create a bools global variable array()") { REQUIRE(parser.process("{global mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); } + + SECTION("create an ints local variable with initializer list") { REQUIRE(parser.process("{local myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, nullptr) == "24"); } + SECTION("create a strings local variable with initializer list") { REQUIRE(parser.process("{local mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); } + SECTION("create a bools local variable with initializer list") { REQUIRE(parser.process("{local mybool = (3*3 == 8, 1 + 1 == 2)}{mybool[1]}", 0, nullptr, nullptr, nullptr) == "true"); } + SECTION("create an ints global variable with initializer list") { REQUIRE(parser.process("{global myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); } + SECTION("create a strings global variable with initializer list") { REQUIRE(parser.process("{global mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); } + SECTION("create a bools global variable with initializer list") { REQUIRE(parser.process("{global mybool = (2*3 == 8, 1 + 1 == 2, 5*5 != 33)}{mybool[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); } + + SECTION("create an ints local variable by a copy") { REQUIRE(parser.process("{local myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357"); } + SECTION("create a strings local variable by a copy") { REQUIRE(parser.process("{local mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes"); } + SECTION("create a bools local variable by a copy") { REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); } + SECTION("create an ints global variable by a copy") { REQUIRE(parser.process("{global myint = temperature}{myint[0]}", 0, &config, nullptr, &context_with_global_dict) == "357"); } + SECTION("create a strings global variable by a copy") { REQUIRE(parser.process("{global mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, &context_with_global_dict) == "testnotes"); } + SECTION("create a bools global variable by a copy") { REQUIRE(parser.process("{global mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, &context_with_global_dict) == "true"); } + + SECTION("create an ints local variable by a copy and overwrite it") { + REQUIRE(parser.process("{local myint = temperature}{myint = array(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24"); + REQUIRE(parser.process("{local myint = temperature}{myint = (2*3, 4*6)}{myint[1]}", 0, &config, nullptr, nullptr) == "24"); + REQUIRE(parser.process("{local myint = temperature}{myint = (1)}{myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357"); + } + SECTION("create a strings local variable by a copy and overwrite it") { + REQUIRE(parser.process("{local mystr = filament_notes}{mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine"); + REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr[1]}", 0, &config, nullptr, nullptr) == "mineonlymine"); + REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes"); + } + SECTION("create a bools local variable by a copy and overwrite it") { + REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = array(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true"); + REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, true)}{mybool[1]}", 0, &config, nullptr, nullptr) == "true"); + REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, false)}{mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); + } +} From e46891fa8c0fffeb4c33097b4c477a05ca9bb8c0 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 23 Mar 2023 09:23:20 +0100 Subject: [PATCH 122/201] PlaceholderParser: Fixed compilation issues, added integration test with FDM slicing process. --- src/libslic3r/PlaceholderParser.cpp | 9 ++++----- tests/fff_print/test_custom_gcode.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 78ded40c4..55e7235eb 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -737,7 +737,7 @@ namespace client // Table to translate symbol tag to a human readable error message. static std::map tag_to_error_message; - static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; } + static bool evaluate_full_macro(const MyContext *ctx) { return ! ctx->just_boolean_expression; } const ConfigOption* optptr(const t_config_option_key &opt_key) const override { @@ -1570,13 +1570,12 @@ namespace client template struct InterpolateTableContext { - template struct Item { double x; boost::iterator_range it_range_x; double y; }; - std::vector> table; + std::vector table; static void init(const expr &x) { if (!x.numeric_type()) @@ -1785,8 +1784,8 @@ namespace client // Also the start symbol switches between the "full macro syntax" and a "boolean expression only", // depending on the context->just_boolean_expression flag. This way a single static expression parser // could serve both purposes. - start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] > - ( (eps(_a==true) > text_block(_r1) [_val=_1]) + start = + ( (eps(px::bind(&MyContext::evaluate_full_macro, _r1)) > text_block(_r1) [_val=_1]) | conditional_expression(_r1) [ px::bind(&expr::evaluate_boolean_to_string, _1, _val) ] ) > eoi; start.name("start"); diff --git a/tests/fff_print/test_custom_gcode.cpp b/tests/fff_print/test_custom_gcode.cpp index 37103b316..0d109070b 100644 --- a/tests/fff_print/test_custom_gcode.cpp +++ b/tests/fff_print/test_custom_gcode.cpp @@ -258,4 +258,16 @@ SCENARIO("Custom G-code", "[CustomGCode]") REQUIRE(match_count == 2); } } + GIVEN("before_layer_gcode increments global variable") { + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "start_gcode", "{global counter=0}" }, + { "before_layer_gcode", ";Counter{counter=counter+1;counter}\n" } + }); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("The counter is emitted multiple times before layer change.") { + REQUIRE(Slic3r::Test::contains(gcode, ";Counter1\n")); + REQUIRE(Slic3r::Test::contains(gcode, ";Counter2\n")); + REQUIRE(Slic3r::Test::contains(gcode, ";Counter3\n")); + } + } } From 6b1c9119bed3e88534beeebbf30870b02fe0d157 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 15 Feb 2023 16:27:03 +0100 Subject: [PATCH 123/201] Add code to extract sla supports outline for arrangement Add functionality to libnest to support on_packed handler for all items. Use it to swap arrange polygon from envelope to core poly after packing was succesful. Solving brim issue by offset adjustments of arrange polygons Only in sla yet --- src/libnest2d/include/libnest2d/nester.hpp | 18 +++++ .../include/libnest2d/placers/nfpplacer.hpp | 1 + src/libslic3r/Arrange.cpp | 4 ++ src/libslic3r/Arrange.hpp | 2 +- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 68 ++++++++++++++++++- src/slic3r/GUI/Jobs/ArrangeJob.hpp | 2 + 6 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/libnest2d/include/libnest2d/nester.hpp b/src/libnest2d/include/libnest2d/nester.hpp index 52c738a4c..78da28759 100644 --- a/src/libnest2d/include/libnest2d/nester.hpp +++ b/src/libnest2d/include/libnest2d/nester.hpp @@ -70,6 +70,7 @@ class _Item { int binid_{BIN_ID_UNSET}, priority_{0}; bool fixed_{false}; + std::function on_packed_; public: @@ -205,6 +206,23 @@ public: sl::vertex(sh_, idx) = v; } + void setShape(RawShape rsh) + { + sh_ = std::move(rsh); + invalidateCache(); + } + + void setOnPackedFn(std::function onpackedfn) + { + on_packed_ = onpackedfn; + } + + void onPacked() + { + if (on_packed_) + on_packed_(*this); + } + /** * @brief Calculate the shape area. * diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index a17d54982..0ef0929e1 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -901,6 +901,7 @@ public: if(can_pack) { ret = PackResult(item); + item.onPacked(); merged_pile_ = nfp::merge(merged_pile_, item.transformedShape()); } else { ret = PackResult(best_overfit); diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 31751ab32..b3293e17f 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -583,8 +583,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly, outp.emplace_back(std::move(p)); outp.back().rotation(rotation); outp.back().translation({offs.x(), offs.y()}); + outp.back().inflate(arrpoly.inflation); outp.back().binId(arrpoly.bed_idx); outp.back().priority(arrpoly.priority); + outp.back().setOnPackedFn([&arrpoly](Item &itm){ + itm.inflate(-arrpoly.inflation); + }); } template auto call_with_bed(const Points &bed, Fn &&fn) diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 6d4001a50..4ed00668c 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -71,7 +71,7 @@ static const constexpr int UNARRANGED = -1; /// polygon belongs: UNARRANGED means no place for the polygon /// (also the initial state before arrange), 0..N means the index of the bed. /// Zero is the physical bed, larger than zero means a virtual bed. -struct ArrangePolygon { +struct ArrangePolygon { ExPolygon poly; /// The 2D silhouette to be arranged Vec2crd translation{0, 0}; /// The translation of the poly double rotation{0.0}; /// The rotation of the poly in radians diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 4595ae254..72a938ab3 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -2,6 +2,8 @@ #include "libslic3r/BuildVolume.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" @@ -11,6 +13,7 @@ #include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/format.hpp" + #include "libnest2d/common.hpp" #include @@ -76,6 +79,7 @@ void ArrangeJob::clear_input() m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); m_unprintable.reserve(cunprint /* for optional wti */); + m_min_inflation = 0; } void ArrangeJob::prepare_all() { @@ -145,6 +149,50 @@ void ArrangeJob::prepare_selected() { for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; } +static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, + const SLAPrintObject &po, + const ModelInstance &inst, + coord_t min_infl) +{ + auto laststep = po.last_completed_step(); + + if (laststep < slaposCount && laststep > slaposSupportTree) { + auto omesh = po.get_mesh_to_print(); + auto &smesh = po.support_mesh(); + + Vec3d rotation = inst.get_rotation(); + rotation.z() = 0.; + Transform3f trafo_instance = + Geometry::assemble_transform(inst.get_offset().z() * Vec3d::UnitZ(), + rotation, + inst.get_scaling_factor(), + inst.get_mirror()).cast(); + + trafo_instance = trafo_instance * po.trafo().cast().inverse(); + + auto polys = reserve_vector(3); + auto zlvl = -po.get_elevation(); + + if (omesh) { + polys.emplace_back(its_convex_hull_2d_above(*omesh, trafo_instance, zlvl)); + ret.poly.contour = polys.back(); + ret.poly.holes = {}; + } + + polys.emplace_back(its_convex_hull_2d_above(smesh.its, trafo_instance, zlvl)); + ret.poly.contour = Geometry::convex_hull(polys); + ret.poly.holes = {}; + + // The 1.1 multiplier is a safety gap, as the offset might be bigger + // in sharp edges of a polygon, depending on clipper's offset algorithm + coord_t infl = 1.1 * scaled(po.config().pad_brim_size.getFloat() + + po.config().pad_around_object.getBool() * + po.config().pad_object_gap.getFloat()); + + ret.inflation = std::max(infl, min_infl); + } +} + arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) { arrangement::ArrangePolygon ap = get_arrange_poly(mi, m_plater); @@ -156,12 +204,28 @@ arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) m_unarranged.emplace_back(mi); }; + if (m_plater->printer_technology() == ptSLA) { + auto obj_id = mi->get_object()->id(); + const SLAPrintObject *po = + m_plater->sla_print().get_print_object_by_model_object_id(obj_id); + + if (po) { + update_arrangepoly_slaprint(ap, *po, *mi, m_min_inflation); + m_min_inflation = std::max(m_min_inflation, ap.inflation); + } + } else { + // TODO: get fff supports outline + } + return ap; } void ArrangeJob::prepare() { wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); + for (auto &ap : m_selected) { + ap.inflation = m_min_inflation; + } } void ArrangeJob::process(Ctl &ctl) @@ -286,7 +350,9 @@ template<> arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, const Plater * plater) { - return get_arrange_poly(PtrWrapper{inst}, plater); + auto ap = get_arrange_poly(PtrWrapper{inst}, plater); + + return ap; } arrangement::ArrangeParams get_arrange_params(Plater *p) diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.hpp b/src/slic3r/GUI/Jobs/ArrangeJob.hpp index 106cc57dd..4b75868a0 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.hpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.hpp @@ -21,6 +21,8 @@ class ArrangeJob : public Job ArrangePolygons m_selected, m_unselected, m_unprintable; std::vector m_unarranged; + coord_t m_min_inflation = 0; + Plater *m_plater; // clear m_selected and m_unselected, reserve space for next usage From e585fd22218f4679d4ba86fbfbb0b63504854d58 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 23 Mar 2023 10:16:13 +0100 Subject: [PATCH 124/201] Fixed duplication of auxiliary volumes when GLCanvas3D::reload_scene() is called for SLA printers --- src/slic3r/GUI/3DScene.cpp | 32 ++++++++++++++++++-------------- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 843aa6a33..f8b41da8a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -603,24 +603,28 @@ void GLVolumeCollection::load_object_auxiliary( }; // Get the support mesh. - TriangleMesh supports_mesh = print_object->support_mesh(); - if (!supports_mesh.empty()) { - supports_mesh.transform(mesh_trafo_inv); - TriangleMesh convex_hull = supports_mesh.convex_hull_3d(); - for (const std::pair& instance_idx : instances) { - const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; - add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); + if (milestone == SLAPrintObjectStep::slaposSupportTree) { + TriangleMesh supports_mesh = print_object->support_mesh(); + if (!supports_mesh.empty()) { + supports_mesh.transform(mesh_trafo_inv); + TriangleMesh convex_hull = supports_mesh.convex_hull_3d(); + for (const std::pair& instance_idx : instances) { + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); + } } } // Get the pad mesh. - TriangleMesh pad_mesh = print_object->pad_mesh(); - if (!pad_mesh.empty()) { - pad_mesh.transform(mesh_trafo_inv); - TriangleMesh convex_hull = pad_mesh.convex_hull_3d(); - for (const std::pair& instance_idx : instances) { - const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; - add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposPad, pad_mesh, GLVolume::SLA_PAD_COLOR, convex_hull); + if (milestone == SLAPrintObjectStep::slaposPad) { + TriangleMesh pad_mesh = print_object->pad_mesh(); + if (!pad_mesh.empty()) { + pad_mesh.transform(mesh_trafo_inv); + TriangleMesh convex_hull = pad_mesh.convex_hull_3d(); + for (const std::pair& instance_idx : instances) { + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposPad, pad_mesh, GLVolume::SLA_PAD_COLOR, convex_hull); + } } } } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e6b23c4b8..ad1c8fe03 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2162,7 +2162,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } update_volumes_colors_by_extruder(); - // Update selection indices based on the old/new GLVolumeCollection. + // Update selection indices based on the old/new GLVolumeCollection. if (m_selection.get_mode() == Selection::Instance) m_selection.instances_changed(instance_ids_selected); else From 3c5ecd4a8fb473d06fde8555633856239ff81a20 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Feb 2023 16:06:21 +0100 Subject: [PATCH 125/201] FFF arrange minding skirt and brim distances many thanks to @individ-divided and @jschuh --- src/libslic3r/Print.hpp | 5 ++ src/slic3r/GUI/Jobs/ArrangeJob.cpp | 75 +++++++++++++++++++++++------- src/slic3r/GUI/Jobs/ArrangeJob.hpp | 4 +- src/slic3r/GUI/Jobs/FillBedJob.cpp | 22 ++++++++- src/slic3r/GUI/Jobs/FillBedJob.hpp | 1 + 5 files changed, 89 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9fbbe378a..f5712c3de 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -565,6 +565,11 @@ public: SpanOfConstPtrs objects() const { return SpanOfConstPtrs(const_cast(m_objects.data()), m_objects.size()); } PrintObject* get_object(size_t idx) { return const_cast(m_objects[idx]); } const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } + const PrintObject* get_print_object_by_model_object_id(ObjectID object_id) const { + auto it = std::find_if(m_objects.begin(), m_objects.end(), + [object_id](const PrintObject* obj) { return obj->model_object()->id() == object_id; }); + return (it == m_objects.end()) ? nullptr : *it; + } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. const PrintObject* get_object(ObjectID object_id) const { diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 72a938ab3..67c6298f8 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -2,6 +2,7 @@ #include "libslic3r/BuildVolume.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" @@ -79,7 +80,6 @@ void ArrangeJob::clear_input() m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); m_unprintable.reserve(cunprint /* for optional wti */); - m_min_inflation = 0; } void ArrangeJob::prepare_all() { @@ -151,8 +151,7 @@ void ArrangeJob::prepare_selected() { static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, const SLAPrintObject &po, - const ModelInstance &inst, - coord_t min_infl) + const ModelInstance &inst) { auto laststep = po.last_completed_step(); @@ -189,10 +188,22 @@ static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, po.config().pad_around_object.getBool() * po.config().pad_object_gap.getFloat()); - ret.inflation = std::max(infl, min_infl); + ret.inflation = infl; } } +static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst) +{ + const BrimType brim_type = po.config().brim_type.value; + const float brim_separation = po.config().brim_separation.getFloat(); + const float brim_width = po.config().brim_width.getFloat(); + const bool has_outer_brim = brim_type == BrimType::btOuterOnly || + brim_type == BrimType::btOuterAndInner; + + // How wide is the brim? (in scaled units) + return has_outer_brim ? scaled(brim_width + brim_separation) : 0; +} + arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) { arrangement::ArrangePolygon ap = get_arrange_poly(mi, m_plater); @@ -204,27 +215,40 @@ arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) m_unarranged.emplace_back(mi); }; - if (m_plater->printer_technology() == ptSLA) { - auto obj_id = mi->get_object()->id(); - const SLAPrintObject *po = - m_plater->sla_print().get_print_object_by_model_object_id(obj_id); + return ap; +} - if (po) { - update_arrangepoly_slaprint(ap, *po, *mi, m_min_inflation); - m_min_inflation = std::max(m_min_inflation, ap.inflation); - } - } else { - // TODO: get fff supports outline +coord_t get_skirt_offset(const Plater* plater) { + float skirt_inset = 0.f; + // Try to subtract the skirt from the bed shape so we don't arrange outside of it. + if (plater->printer_technology() == ptFFF && plater->fff_print().has_skirt()) { + const auto& print = plater->fff_print(); + skirt_inset = print.config().skirts.value * print.skirt_flow().width() + + print.config().skirt_distance.value; } - return ap; + return scaled(skirt_inset); } void ArrangeJob::prepare() { wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); + + coord_t min_offset = 0; for (auto &ap : m_selected) { - ap.inflation = m_min_inflation; + min_offset = std::max(ap.inflation, min_offset); + } + + if (m_plater->printer_technology() == ptSLA) { + // Apply the max offset for all the objects + for (auto &ap : m_selected) { + ap.inflation = min_offset; + } + } else { // it's fff, brims only need to be minded from bed edges + for (auto &ap : m_selected) { + ap.inflation = 0; + } + m_min_bed_inset = min_offset; } } @@ -238,6 +262,8 @@ void ArrangeJob::process(Ctl &ctl) prepare(); params = get_arrange_params(m_plater); get_bed_shape(*m_plater->config(), bed); + coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset; + params.min_bed_distance = std::max(params.min_bed_distance, min_inset); }).wait(); auto count = unsigned(m_selected.size() + m_unprintable.size()); @@ -352,6 +378,23 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, { auto ap = get_arrange_poly(PtrWrapper{inst}, plater); + auto obj_id = inst->get_object()->id(); + if (plater->printer_technology() == ptSLA) { + const SLAPrintObject *po = + plater->sla_print().get_print_object_by_model_object_id(obj_id); + + if (po) { + update_arrangepoly_slaprint(ap, *po, *inst); + } + } else { + const PrintObject *po = + plater->fff_print().get_print_object_by_model_object_id(obj_id); + + if (po) { + ap.inflation = brim_offset(*po, *inst); + } + } + return ap; } diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.hpp b/src/slic3r/GUI/Jobs/ArrangeJob.hpp index 4b75868a0..a422d4407 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.hpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.hpp @@ -21,7 +21,7 @@ class ArrangeJob : public Job ArrangePolygons m_selected, m_unselected, m_unprintable; std::vector m_unarranged; - coord_t m_min_inflation = 0; + coord_t m_min_bed_inset = 0.; Plater *m_plater; @@ -104,6 +104,8 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, arrangement::ArrangeParams get_arrange_params(Plater *p); +coord_t get_skirt_offset(const Plater* plater); + }} // namespace Slic3r::GUI #endif // ARRANGEJOB_HPP diff --git a/src/slic3r/GUI/Jobs/FillBedJob.cpp b/src/slic3r/GUI/Jobs/FillBedJob.cpp index 6a173683f..b8a579ed5 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.cpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.cpp @@ -18,6 +18,7 @@ void FillBedJob::prepare() m_selected.clear(); m_unselected.clear(); m_bedpts.clear(); + m_min_bed_inset = 0.; m_object_idx = m_plater->get_selected_object_idx(); if (m_object_idx == -1) @@ -29,7 +30,7 @@ void FillBedJob::prepare() m_selected.reserve(model_object->instances.size()); for (ModelInstance *inst : model_object->instances) if (inst->printable) { - ArrangePolygon ap = get_arrange_poly(PtrWrapper{inst}, m_plater); + ArrangePolygon ap = get_arrange_poly(inst, m_plater); // Existing objects need to be included in the result. Only // the needed amount of object will be added, no more. ++ap.priority; @@ -101,6 +102,23 @@ void FillBedJob::prepare() for (auto &p : m_unselected) if (p.bed_idx > 0) p.translation(X) -= p.bed_idx * stride; + + coord_t min_offset = 0; + for (auto &ap : m_selected) { + min_offset = std::max(ap.inflation, min_offset); + } + + if (m_plater->printer_technology() == ptSLA) { + // Apply the max offset for all the objects + for (auto &ap : m_selected) { + ap.inflation = min_offset; + } + } else { // it's fff, brims only need to be minded from bed edges + for (auto &ap : m_selected) { + ap.inflation = 0; + } + m_min_bed_inset = min_offset; + } } void FillBedJob::process(Ctl &ctl) @@ -110,6 +128,8 @@ void FillBedJob::process(Ctl &ctl) ctl.call_on_main_thread([this, ¶ms] { prepare(); params = get_arrange_params(m_plater); + coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset; + params.min_bed_distance = std::max(params.min_bed_distance, min_inset); }).wait(); ctl.update_status(0, statustxt); diff --git a/src/slic3r/GUI/Jobs/FillBedJob.hpp b/src/slic3r/GUI/Jobs/FillBedJob.hpp index b1417bbbd..b953e0e98 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.hpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.hpp @@ -16,6 +16,7 @@ class FillBedJob : public Job ArrangePolygons m_selected; ArrangePolygons m_unselected; + coord_t m_min_bed_inset = 0.; Points m_bedpts; From 333d0727efc0f66235c1bbf4269c105d80b6cfb4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Feb 2023 14:10:23 +0100 Subject: [PATCH 126/201] Handling brim offset in SLA without the need to slice the object --- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 48 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 67c6298f8..8115136a5 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -97,28 +97,28 @@ void ArrangeJob::prepare_all() { void ArrangeJob::prepare_selected() { clear_input(); - + Model &model = m_plater->model(); double stride = bed_stride(m_plater); - + std::vector obj_sel(model.objects.size(), nullptr); for (auto &s : m_plater->get_selection().get_content()) if (s.first < int(obj_sel.size())) obj_sel[size_t(s.first)] = &s.second; - + // Go through the objects and check if inside the selection for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { const Selection::InstanceIdxsList * instlist = obj_sel[oidx]; ModelObject *mo = model.objects[oidx]; - + std::vector inst_sel(mo->instances.size(), false); - + if (instlist) for (auto inst_id : *instlist) inst_sel[size_t(inst_id)] = true; - + for (size_t i = 0; i < inst_sel.size(); ++i) { ModelInstance * mi = mo->instances[i]; ArrangePolygon &&ap = get_arrange_poly_(mi); @@ -127,11 +127,11 @@ void ArrangeJob::prepare_selected() { (inst_sel[i] ? m_selected : m_unselected) : m_unprintable; - + cont.emplace_back(std::move(ap)); } } - + if (auto wti = get_wipe_tower(*m_plater)) { ArrangePolygon &&ap = get_arrange_poly(wti, m_plater); @@ -139,20 +139,34 @@ void ArrangeJob::prepare_selected() { m_unselected; cont.emplace_back(std::move(ap)); } - + // If the selection was empty arrange everything - if (m_selected.empty()) m_selected.swap(m_unselected); - + if (m_selected.empty()) + m_selected.swap(m_unselected); + // The strides have to be removed from the fixed items. For the // arrangeable (selected) items bed_idx is ignored and the // translation is irrelevant. - for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; + for (auto &p : m_unselected) + p.translation(X) -= p.bed_idx * stride; } static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, const SLAPrintObject &po, const ModelInstance &inst) { + // The 1.1 multiplier is a safety gap, as the offset might be bigger + // in sharp edges of a polygon, depending on clipper's offset algorithm + coord_t pad_infl = 0; + { + double infl = po.config().pad_enable.getBool() * ( + po.config().pad_brim_size.getFloat() + + po.config().pad_around_object.getBool() * + po.config().pad_object_gap.getFloat() ); + + pad_infl = scaled(1.1 * infl); + } + auto laststep = po.last_completed_step(); if (laststep < slaposCount && laststep > slaposSupportTree) { @@ -181,15 +195,9 @@ static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, polys.emplace_back(its_convex_hull_2d_above(smesh.its, trafo_instance, zlvl)); ret.poly.contour = Geometry::convex_hull(polys); ret.poly.holes = {}; - - // The 1.1 multiplier is a safety gap, as the offset might be bigger - // in sharp edges of a polygon, depending on clipper's offset algorithm - coord_t infl = 1.1 * scaled(po.config().pad_brim_size.getFloat() + - po.config().pad_around_object.getBool() * - po.config().pad_object_gap.getFloat()); - - ret.inflation = infl; } + + ret.inflation = pad_infl; } static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst) From 92e0c1351319d2a8e2d4f5c399d3694a85e1be2d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Feb 2023 14:10:59 +0100 Subject: [PATCH 127/201] Try to handle fff supports in arrange after slicing Not really precise yet --- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 45 +++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 8115136a5..826dc2407 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -212,6 +212,49 @@ static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst) return has_outer_brim ? scaled(brim_width + brim_separation) : 0; } +template +Polygon support_layers_chull (Points &pts, It from_lyr, It to_lyr) { + + size_t cap = 0; + for (auto it = from_lyr; it != to_lyr; ++it) { + for (const ExPolygon &expoly : (*it)->support_islands) + cap += expoly.contour.points.size(); + } + + pts.reserve(pts.size() + cap); + + for (auto it = from_lyr; it != to_lyr; ++it) { + for (const ExPolygon &expoly : (*it)->support_islands) + std::copy(expoly.contour.begin(), expoly.contour.end(), + std::back_inserter(pts)); + } + + Polygon ret = Geometry::convex_hull(pts); + + return ret; +} + +static void update_arrangepoly_fffprint(arrangement::ArrangePolygon &ret, + const PrintObject &po, + const ModelInstance &inst) +{ + auto laststep = po.last_completed_step(); + + coord_t infl = brim_offset(po, inst); + + if (laststep < posCount && laststep > posSupportMaterial) { + Points pts = std::move(ret.poly.contour.points); + Polygon poly = support_layers_chull(pts, + po.support_layers().begin(), + po.support_layers().end()); + + ret.poly.contour = std::move(poly); + ret.poly.holes = {}; + } + + ret.inflation = infl; +} + arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) { arrangement::ArrangePolygon ap = get_arrange_poly(mi, m_plater); @@ -399,7 +442,7 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, plater->fff_print().get_print_object_by_model_object_id(obj_id); if (po) { - ap.inflation = brim_offset(*po, *inst); + update_arrangepoly_fffprint(ap, *po, *inst); } } From 84284b44189d73fd57c6a34a80fcea9403859b6c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 23 Mar 2023 11:42:46 +0100 Subject: [PATCH 128/201] Fixed crash when scaling object to fit print volume with SLA printers --- src/slic3r/GUI/GLCanvas3D.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ad1c8fe03..a433f6dbd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2119,10 +2119,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) { - if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { - const SLAPrintObject* po = sla_print->objects()[volume->object_idx()]; - float zoffs = po->get_current_elevation() / sla_print->relative_correction().z(); - volume->set_sla_shift_z(zoffs); + const ModelObject* model_object = (volume->object_idx() < (int)m_model->objects.size()) ? m_model->objects[volume->object_idx()] : nullptr; + if (model_object != nullptr && model_object->instances[volume->instance_idx()]->is_printable()) { + const SLAPrintObject* po = sla_print->get_print_object_by_model_object_id(model_object->id()); + if (po != nullptr) + volume->set_sla_shift_z(po->get_current_elevation() / sla_print->relative_correction().z()); } } } From 7df4cfb45334e0773a1f9591884febdda6d9aa17 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 23 Mar 2023 12:27:07 +0100 Subject: [PATCH 129/201] Fixed a crash when selection wipe tower and using add instance function: this was between 2.6.0-alpha3 and 2.6.0-alpha4 in 4d5b85e. --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a64de49ee..5c8677d0a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4779,7 +4779,7 @@ bool Plater::priv::can_increase_instances() const if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false; const auto obj_idxs = get_selection().get_object_idxs(); - return !obj_idxs.empty() && !sidebar->obj_list()->has_selected_cut_object(); + return !obj_idxs.empty() && !get_selection().is_wipe_tower() && !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_decrease_instances(int obj_idx /*= -1*/) const From cd70954758e55116ca90540cba32a607b6e9f8a4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 22 Feb 2023 13:13:22 +0100 Subject: [PATCH 130/201] Wipe tower: added an option to change spacing of the lines --- src/libslic3r/GCode/WipeTower.cpp | 3 +-- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 9 +++++++++ src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 8 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 4f4871366..99cbbe87c 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -512,6 +512,7 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector> & if (m_plan.empty()) return; - m_extra_spacing = 1.f; - plan_tower(); for (int i=0;i<5;++i) { save_on_last_wipe(); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 09514200f..d9ed8af96 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -461,7 +461,7 @@ static std::vector s_Preset_print_options { "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", - "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", + "wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "wall_distribution_count", "min_feature_size", "min_bead_width" }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4030e381a..af897b93c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -206,6 +206,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "wipe_tower_width" || opt_key == "wipe_tower_brim_width" || opt_key == "wipe_tower_bridging" + || opt_key == "wipe_tower_extra_spacing" || opt_key == "wipe_tower_no_sparse_layers" || opt_key == "wiping_volumes_matrix" || opt_key == "parking_pos_retraction" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index dbdb36c7d..fcb78bc53 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3150,6 +3150,15 @@ void PrintConfigDef::init_fff_params() def->min = 0.; def->set_default_value(new ConfigOptionFloat(2.)); + def = this->add("wipe_tower_extra_spacing", coPercent); + def->label = L("Wipe tower purge lines spacing"); + def->tooltip = L("Spacing of purge lines on the wipe tower."); + def->sidetext = L("%"); + def->mode = comExpert; + def->min = 100.; + def->max = 200.; + def->set_default_value(new ConfigOptionPercent(100.)); + def = this->add("wipe_into_infill", coBool); def->category = L("Wipe options"); def->label = L("Wipe into this object's infill"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index e5a9f4c41..4027eef36 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -823,6 +823,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloat, wipe_tower_per_color_wipe)) ((ConfigOptionFloat, wipe_tower_rotation_angle)) ((ConfigOptionFloat, wipe_tower_brim_width)) + ((ConfigOptionPercent, wipe_tower_extra_spacing)) ((ConfigOptionFloat, wipe_tower_bridging)) ((ConfigOptionFloats, wiping_volumes_matrix)) ((ConfigOptionFloats, wiping_volumes_extruders)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index c8c3923f7..5312d52ec 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -320,7 +320,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_wipe_tower = config->opt_bool("wipe_tower"); for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", - "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" }) + "wipe_tower_extra_spacing", "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" }) toggle_field(el, have_wipe_tower); toggle_field("avoid_crossing_curled_overhangs", !config->opt_bool("avoid_crossing_perimeters")); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5c8677d0a..6ecfa2339 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2016,7 +2016,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ "bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", - "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", + "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_extra_spacing", "extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 41a015e9f..3f479030b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1602,6 +1602,7 @@ void TabPrint::build() optgroup->append_single_option_line("wipe_tower_rotation_angle"); optgroup->append_single_option_line("wipe_tower_brim_width"); optgroup->append_single_option_line("wipe_tower_bridging"); + optgroup->append_single_option_line("wipe_tower_extra_spacing"); optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); optgroup->append_single_option_line("single_extruder_multi_material_priming"); From 571b133791b0af6789df2df49ea0cf86783d210b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 1 Mar 2023 10:48:05 +0100 Subject: [PATCH 131/201] Wipe tower: stabilization cone --- src/libslic3r/GCode/WipeTower.cpp | 113 +++++++++++++++++++++++++----- src/libslic3r/GCode/WipeTower.hpp | 4 ++ src/libslic3r/Print.cpp | 30 +++++--- src/libslic3r/Print.hpp | 1 + src/slic3r/GUI/3DScene.cpp | 13 ++++ 5 files changed, 135 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 99cbbe87c..516ae3d8a 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -10,6 +10,7 @@ #include "GCodeProcessor.hpp" #include "BoundingBox.hpp" #include "LocalesUtils.hpp" +#include "Geometry.hpp" #include @@ -1170,35 +1171,94 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() ";------------------\n\n\n\n\n\n\n"); } - // outer perimeter (always): - writer.rectangle(wt_box, feedrate); + // This block creates the stabilization cone. + // First define a lambda to draw the rectangle with stabilization. + auto supported_rectangle = [this, &writer](const box_coordinates& wt_box, double feedrate) -> Polygon { + const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth); + + double r = std::tan(Geometry::deg2rad(15.)) * (m_wipe_tower_height - m_layer_info->z); + Vec2f center = (wt_box.lu + wt_box.rd) / 2.; + double w = wt_box.lu.y() - wt_box.ld.y(); + enum Type { + Arc, + Corner, + ArcStart, + ArcEnd + }; + + std::vector> pts = {{wt_box.ru, Corner}}; + if (double alpha_start = std::asin((0.5*w)/r); ! std::isnan(alpha_start) && r > 0.5*w+0.01) { + for (double alpha = alpha_start; alpha < M_PI-alpha_start+0.001; alpha+=(M_PI-2*alpha_start) / 20.) + pts.emplace_back(Vec2f(center.x() + r*std::cos(alpha)/support_scale, center.y() + r*std::sin(alpha)), alpha == alpha_start ? ArcStart : Arc); + pts.back().second = ArcEnd; + } + pts.emplace_back(wt_box.lu, Corner); + pts.emplace_back(wt_box.ld, Corner); + for (int i=int(pts.size())-3; i>0; --i) + pts.emplace_back(Vec2f(pts[i].first.x(), 2*center.y()-pts[i].first.y()), i == int(pts.size())-3 ? ArcStart : i == 1 ? ArcEnd : Arc); + pts.emplace_back(wt_box.rd, Corner); + + // Find the closest corner and travel to it. + int start_i = 0; + double min_dist = std::numeric_limits::max(); + for (int i=0; i()); + for (int i=cp+1; true; ++i ) { + if (i==int(poly.points.size())) + i = 0; + writer.extrude(unscale(poly.points[i]).cast()); + if (i == cp) + break; + } } // Save actual brim width to be later passed to the Print object, which will use it // for skirt calculation and pass it to GLCanvas for precise preview box - m_wipe_tower_brim_width_real = wt_box.ld.x() - box.ld.x() + spacing/2.f; - wt_box = box; + m_wipe_tower_brim_width_real = loops_num * spacing; } - // Now prepare future wipe. box contains rectangle that was extruded last (ccw). - Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : - (writer.pos() == wt_box.rd ? wt_box.ru : - (writer.pos() == wt_box.ru ? wt_box.lu : - wt_box.ld))); - writer.add_wipe_point(writer.pos()) - .add_wipe_point(target); - + // Now prepare future wipe. + int i = poly.closest_point_index(Point::new_scale(writer.x(), writer.y())); + writer.add_wipe_point(writer.pos()); + writer.add_wipe_point(unscale(poly.points[i==0 ? int(poly.points.size())-1 : i-1]).cast()); // Ask our writer about how much material was consumed. // Skip this in case the layer is sparse and config option to not print sparse layers is enabled. @@ -1209,6 +1269,24 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() return construct_tcr(writer, false, old_tool); } +// Static method to get the radius and x-scaling of the stabilizing cone base. +std::pair WipeTower::get_wipe_tower_cone_base(double width, double height, double depth) +{ + const double angle_deg = 15.; + double R = std::tan(Geometry::deg2rad(angle_deg)) * height; + double fake_width = 0.66 * width; + double diag = std::hypot(fake_width / 2., depth / 2.); + double support_scale = 1.; + if (R > diag) { + double w = fake_width; + double sin = 0.5 * depth / diag; + double tan = depth / w; + double t = (R - diag) * sin; + support_scale = (w / 2. + t / tan + t * tan) / (w / 2.); + } + return std::make_pair(R, support_scale); +} + // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume) @@ -1251,6 +1329,7 @@ void WipeTower::plan_tower() m_wipe_tower_depth = 0.f; for (auto& layer : m_plan) layer.depth = 0.f; + m_wipe_tower_height = m_plan.empty() ? 0.f : m_plan.back().z; for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { @@ -1357,7 +1436,7 @@ void WipeTower::generate(std::vector> & m_old_temperature = -1; // reset last temperature written in the gcode std::vector layer_result; - for (auto layer : m_plan) + for (const WipeTower::WipeTowerInfo& layer : m_plan) { set_layer(layer.z, layer.height, 0, false/*layer.z == m_plan.front().z*/, layer.z == m_plan.back().z); m_internal_rotation += 180.f; diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 7f4a6bf9a..e23c6645f 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -23,6 +23,8 @@ class WipeTower public: static const std::string never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; } + static std::pair get_wipe_tower_cone_base(double width, double height, double depth); + struct Extrusion { Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} @@ -142,6 +144,7 @@ public: float get_depth() const { return m_wipe_tower_depth; } float get_brim_width() const { return m_wipe_tower_brim_width_real; } + float get_wipe_tower_height() const { return m_wipe_tower_height; } @@ -253,6 +256,7 @@ private: Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower + float m_wipe_tower_height = 0.f; float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index af897b93c..49dbfea1f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1134,23 +1134,34 @@ Polygons Print::first_layer_islands() const std::vector Print::first_layer_wipe_tower_corners() const { - std::vector corners; + std::vector pts_scaled; + if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); - for (Vec2d pt : { - pt0, - Vec2d(pt0.x()+width, pt0.y() ), - Vec2d(pt0.x()+width, pt0.y()+depth), - Vec2d(pt0.x(), pt0.y()+depth) - }) { + + // First the corners. + std::vector pts = { pt0, + Vec2d(pt0.x()+width, pt0.y()), + Vec2d(pt0.x()+width, pt0.y()+depth), + Vec2d(pt0.x(),pt0.y()+depth) + }; + + // Now the stabilization cone. + Vec2d center = (pts[0] + pts[2])/2.; + const auto [cone_R, cone_x_scale] = WipeTower::get_wipe_tower_cone_base(m_config.wipe_tower_width, m_wipe_tower_data.height, m_wipe_tower_data.depth); + double r = cone_R + m_wipe_tower_data.brim_width; + for (double alpha = 0.; alpha<2*M_PI; alpha += M_PI/20.) + pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha))); + + for (Vec2d& pt : pts) { pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); - corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); + pts_scaled.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); } } - return corners; + return pts_scaled; } void Print::finalize_first_layer_convex_hull() @@ -1448,6 +1459,7 @@ void Print::_make_wipe_tower() wipe_tower.generate(m_wipe_tower_data.tool_changes); m_wipe_tower_data.depth = wipe_tower.get_depth(); m_wipe_tower_data.brim_width = wipe_tower.get_brim_width(); + m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height(); // Unload the current filament over the purge tower. coordf_t layer_height = m_objects.front()->config().layer_height.value; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9fbbe378a..60ef1403f 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -434,6 +434,7 @@ struct WipeTowerData // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: float depth; float brim_width; + float height; void clear() { priming.reset(nullptr); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index c25bff45d..4659bf0f0 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -544,6 +544,19 @@ int GLVolumeCollection::load_wipe_tower_preview( brim_mesh.translate(-brim_width, -brim_width, 0.f); mesh.merge(brim_mesh); + // Now the stabilization cone and its base. + const auto [R, scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth); + TriangleMesh cone_mesh(its_make_cone(R, height)); + cone_mesh.scale(Vec3f(1.f/scale_x, 1.f, 1.f)); + + TriangleMesh disk_mesh(its_make_cylinder(R, brim_height)); + disk_mesh.scale(Vec3f(1. / scale_x, 1., 1.)); // Now it matches the base, which may be elliptic. + disk_mesh.scale(Vec3f(1.f + scale_x*brim_width/R, 1.f + brim_width/R, 1.f)); // Scale so the brim is not deformed. + cone_mesh.merge(disk_mesh); + cone_mesh.translate(width / 2., depth / 2., 0.); + mesh.merge(cone_mesh); + + volumes.emplace_back(new GLVolume(color)); GLVolume& v = *volumes.back(); #if ENABLE_OPENGL_ES From 26ba74fad06fe37325c209f32e69a1ef4f61fe7a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 7 Mar 2023 17:27:07 +0100 Subject: [PATCH 132/201] Wipe tower: stabilization cone bottom infill --- src/libslic3r/GCode/WipeTower.cpp | 70 ++++++++++++++++++++++++++----- src/libslic3r/Print.cpp | 2 +- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 516ae3d8a..fdc17c1ff 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -4,13 +4,17 @@ #include #include #include +#include #include #include +#include "ClipperUtils.hpp" #include "GCodeProcessor.hpp" #include "BoundingBox.hpp" #include "LocalesUtils.hpp" #include "Geometry.hpp" +#include "Surface.hpp" +#include "Fill/FillRectilinear.hpp" #include @@ -1171,9 +1175,11 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() ";------------------\n\n\n\n\n\n\n"); } + const float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4); + // This block creates the stabilization cone. // First define a lambda to draw the rectangle with stabilization. - auto supported_rectangle = [this, &writer](const box_coordinates& wt_box, double feedrate) -> Polygon { + auto supported_rectangle = [this, &writer, spacing](const box_coordinates& wt_box, double feedrate, bool infill_cone) -> Polygon { const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth); double r = std::tan(Geometry::deg2rad(15.)) * (m_wipe_tower_height - m_layer_info->z); @@ -1186,6 +1192,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() ArcEnd }; + // First generate vector of annotated point which form the boundary. std::vector> pts = {{wt_box.ru, Corner}}; if (double alpha_start = std::asin((0.5*w)/r); ! std::isnan(alpha_start) && r > 0.5*w+0.01) { for (double alpha = alpha_start; alpha < M_PI-alpha_start+0.001; alpha+=(M_PI-2*alpha_start) / 20.) @@ -1198,6 +1205,40 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() pts.emplace_back(Vec2f(pts[i].first.x(), 2*center.y()-pts[i].first.y()), i == int(pts.size())-3 ? ArcStart : i == 1 ? ArcEnd : Arc); pts.emplace_back(wt_box.rd, Corner); + // Create a Polygon from the points. + Polygon poly; + for (const auto& [pt, tag] : pts) + poly.points.push_back(Point::new_scale(pt)); + + // Prepare polygons to be filled by infill. + Polylines polylines; + if (infill_cone && m_wipe_tower_width > 2*spacing && m_wipe_tower_depth > 2*spacing) { + ExPolygons infill_areas; + ExPolygon wt_contour(poly); + Polygon wt_rectangle(Points{Point::new_scale(wt_box.ld), Point::new_scale(wt_box.rd), Point::new_scale(wt_box.ru), Point::new_scale(wt_box.lu)}); + wt_rectangle = offset(wt_rectangle, scale_(-spacing/2.)).front(); + wt_contour = offset_ex(wt_contour, scale_(-spacing/2.)).front(); + infill_areas = diff_ex(wt_contour, wt_rectangle); + if (infill_areas.size() == 2) { + ExPolygon& bottom_expoly = infill_areas.front().contour.points.front().y() < infill_areas.back().contour.points.front().y() ? infill_areas[0] : infill_areas[1]; + std::unique_ptr filler(Fill::new_from_type(ipMonotonicLines)); + filler->angle = Geometry::deg2rad(45.f); + filler->spacing = spacing; + FillParams params; + params.density = 1.f; + Surface surface(stBottom, bottom_expoly); + filler->bounding_box = get_extents(bottom_expoly); + polylines = filler->fill_surface(&surface, params); + if (! polylines.empty()) { + if (polylines.front().points.front().x() > polylines.back().points.back().x()) { + std::reverse(polylines.begin(), polylines.end()); + for (Polyline& p : polylines) + p.reverse(); + } + } + } + } + // Find the closest corner and travel to it. int start_i = 0; double min_dist = std::numeric_limits::max(); @@ -1212,29 +1253,38 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() } writer.travel(pts[start_i].first); - // Now actually extrude the boundary: + // Now actually extrude the boundary (and possibly infill): int i = start_i+1 == int(pts.size()) ? 0 : start_i + 1; while (i != start_i) { writer.extrude(pts[i].first, feedrate); + if (pts[i].second == ArcEnd) { + // Extrude the infill. + if (! polylines.empty()) { + // Extrude the infill and travel back to where we were. + bool mirror = ((pts[i].first.y() - center.y()) * (unscale(polylines.front().points.front()).y() - center.y())) < 0.; + for (const Polyline& line : polylines) { + writer.travel(center - (mirror ? 1.f : -1.f) * (unscale(line.points.front()).cast() - center)); + for (size_t i=0; i() - center)); + } + writer.travel(pts[i].first); + } + } if (++i == int(pts.size())) i = 0; } writer.extrude(pts[start_i].first, feedrate); - - // Return the polygon. - Polygon out; - for (const auto& [pt, tag] : pts) - out.points.push_back(Point::new_scale(pt)); - return out; + return poly; }; // outer contour (always) - Polygon poly = supported_rectangle(wt_box, feedrate); + bool infill_cone = first_layer && m_wipe_tower_width > 2*spacing && m_wipe_tower_depth > 2*spacing; + Polygon poly = supported_rectangle(wt_box, feedrate, infill_cone); + // brim (first layer only) if (first_layer) { box_coordinates box = wt_box; - float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4); size_t loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing; for (size_t i = 0; i < loops_num; ++ i) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 49dbfea1f..9d9f46cff 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -591,7 +591,7 @@ std::string Print::validate(std::string* warning) const if (*(lh.end()-2) > *(lh_tallest.end()-2)) tallest_object_idx = i; } - } + } if (has_custom_layering) { for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) { From b20325671a4459fd2992474ce312dc75b383c4cd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 16 Mar 2023 10:07:48 +0100 Subject: [PATCH 133/201] Wipe tower: added the stabilization cone into configuration --- src/libslic3r/GCode/WipeTower.cpp | 10 +++++----- src/libslic3r/GCode/WipeTower.hpp | 3 ++- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 3 ++- src/libslic3r/PrintConfig.cpp | 10 ++++++++++ src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/3DScene.cpp | 24 +++++++++++++----------- src/slic3r/GUI/3DScene.hpp | 4 ++-- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 5 +++-- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 13 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index fdc17c1ff..5717aa0bb 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -517,6 +517,7 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector Polygon { - const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth); + const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth, m_wipe_tower_cone_angle); - double r = std::tan(Geometry::deg2rad(15.)) * (m_wipe_tower_height - m_layer_info->z); + double r = std::tan(Geometry::deg2rad(m_wipe_tower_cone_angle/2.f)) * (m_wipe_tower_height - m_layer_info->z); Vec2f center = (wt_box.lu + wt_box.rd) / 2.; double w = wt_box.lu.y() - wt_box.ld.y(); enum Type { @@ -1320,10 +1321,9 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() } // Static method to get the radius and x-scaling of the stabilizing cone base. -std::pair WipeTower::get_wipe_tower_cone_base(double width, double height, double depth) +std::pair WipeTower::get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg) { - const double angle_deg = 15.; - double R = std::tan(Geometry::deg2rad(angle_deg)) * height; + double R = std::tan(Geometry::deg2rad(angle_deg/2.)) * height; double fake_width = 0.66 * width; double diag = std::hypot(fake_width / 2., depth / 2.); double support_scale = 1.; diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index e23c6645f..15b07f3d8 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -23,7 +23,7 @@ class WipeTower public: static const std::string never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; } - static std::pair get_wipe_tower_cone_base(double width, double height, double depth); + static std::pair get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg); struct Extrusion { @@ -257,6 +257,7 @@ private: float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower float m_wipe_tower_height = 0.f; + float m_wipe_tower_cone_angle = 0.f; float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index d9ed8af96..b36427175 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -460,7 +460,7 @@ static std::vector s_Preset_print_options { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", + "wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "wall_distribution_count", "min_feature_size", "min_bead_width" diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 9d9f46cff..f2143857a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -205,6 +205,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "wipe_tower" || opt_key == "wipe_tower_width" || opt_key == "wipe_tower_brim_width" + || opt_key == "wipe_tower_cone_angle" || opt_key == "wipe_tower_bridging" || opt_key == "wipe_tower_extra_spacing" || opt_key == "wipe_tower_no_sparse_layers" @@ -1150,7 +1151,7 @@ std::vector Print::first_layer_wipe_tower_corners() const // Now the stabilization cone. Vec2d center = (pts[0] + pts[2])/2.; - const auto [cone_R, cone_x_scale] = WipeTower::get_wipe_tower_cone_base(m_config.wipe_tower_width, m_wipe_tower_data.height, m_wipe_tower_data.depth); + const auto [cone_R, cone_x_scale] = WipeTower::get_wipe_tower_cone_base(m_config.wipe_tower_width, m_wipe_tower_data.height, m_wipe_tower_data.depth, m_config.wipe_tower_cone_angle); double r = cone_R + m_wipe_tower_data.brim_width; for (double alpha = 0.; alpha<2*M_PI; alpha += M_PI/20.) pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha))); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index fcb78bc53..68e27f4d1 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3150,6 +3150,16 @@ void PrintConfigDef::init_fff_params() def->min = 0.; def->set_default_value(new ConfigOptionFloat(2.)); + def = this->add("wipe_tower_cone_angle", coFloat); + def->label = L("Stabilization cone apex angle"); + def->tooltip = L("Angle at the apex of the cone that is used to stabilize the wipe tower. " + "Larger angle means wider base."); + def->sidetext = L("°"); + def->mode = comAdvanced; + def->min = 0.; + def->max = 90.; + def->set_default_value(new ConfigOptionFloat(0.)); + def = this->add("wipe_tower_extra_spacing", coPercent); def->label = L("Wipe tower purge lines spacing"); def->tooltip = L("Spacing of purge lines on the wipe tower."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4027eef36..52924bf91 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -823,6 +823,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloat, wipe_tower_per_color_wipe)) ((ConfigOptionFloat, wipe_tower_rotation_angle)) ((ConfigOptionFloat, wipe_tower_brim_width)) + ((ConfigOptionFloat, wipe_tower_cone_angle)) ((ConfigOptionPercent, wipe_tower_extra_spacing)) ((ConfigOptionFloat, wipe_tower_bridging)) ((ConfigOptionFloats, wiping_volumes_matrix)) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 4659bf0f0..f5cf9de7a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -480,11 +480,11 @@ int GLVolumeCollection::load_object_volume( #if ENABLE_OPENGL_ES int GLVolumeCollection::load_wipe_tower_preview( - float pos_x, float pos_y, float width, float depth, float height, + float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh) #else int GLVolumeCollection::load_wipe_tower_preview( - float pos_x, float pos_y, float width, float depth, float height, + float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width) #endif // ENABLE_OPENGL_ES { @@ -545,16 +545,18 @@ int GLVolumeCollection::load_wipe_tower_preview( mesh.merge(brim_mesh); // Now the stabilization cone and its base. - const auto [R, scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth); - TriangleMesh cone_mesh(its_make_cone(R, height)); - cone_mesh.scale(Vec3f(1.f/scale_x, 1.f, 1.f)); + const auto [R, scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth, cone_angle); + if (R > 0.) { + TriangleMesh cone_mesh(its_make_cone(R, height)); + cone_mesh.scale(Vec3f(1.f/scale_x, 1.f, 1.f)); - TriangleMesh disk_mesh(its_make_cylinder(R, brim_height)); - disk_mesh.scale(Vec3f(1. / scale_x, 1., 1.)); // Now it matches the base, which may be elliptic. - disk_mesh.scale(Vec3f(1.f + scale_x*brim_width/R, 1.f + brim_width/R, 1.f)); // Scale so the brim is not deformed. - cone_mesh.merge(disk_mesh); - cone_mesh.translate(width / 2., depth / 2., 0.); - mesh.merge(cone_mesh); + TriangleMesh disk_mesh(its_make_cylinder(R, brim_height)); + disk_mesh.scale(Vec3f(1. / scale_x, 1., 1.)); // Now it matches the base, which may be elliptic. + disk_mesh.scale(Vec3f(1.f + scale_x*brim_width/R, 1.f + brim_width/R, 1.f)); // Scale so the brim is not deformed. + cone_mesh.merge(disk_mesh); + cone_mesh.translate(width / 2., depth / 2., 0.); + mesh.merge(cone_mesh); + } volumes.emplace_back(new GLVolume(color)); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 9791a0faf..d685d6489 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -424,10 +424,10 @@ public: #if ENABLE_OPENGL_ES int load_wipe_tower_preview( - float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr); + float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr); #else int load_wipe_tower_preview( - float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); + float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width); #endif // ENABLE_OPENGL_ES GLVolume* new_toolpath_volume(const ColorRGBA& rgba); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 5312d52ec..1bc2280d5 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -319,7 +319,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("standby_temperature_delta", have_ooze_prevention); bool have_wipe_tower = config->opt_bool("wipe_tower"); - for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", + for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" }) toggle_field(el, have_wipe_tower); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 74f7bf50e..62d4314db 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2253,7 +2253,7 @@ void GCodeViewer::load_shells(const Print& print) const WipeTowerData& wipe_tower_data = print.wipe_tower_data(extruders_count); const float depth = wipe_tower_data.depth; const float brim_width = wipe_tower_data.brim_width; - m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_cone_angle, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width); } } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e369e0f40..6924667e6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2035,17 +2035,18 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; const float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; const float bw = dynamic_cast(m_config->option("wipe_tower_brim_width"))->value; + const float ca = dynamic_cast(m_config->option("wipe_tower_cone_angle"))->value; const Print *print = m_process->fff_print(); const float depth = print->wipe_tower_data(extruders_count).depth; #if ENABLE_OPENGL_ES int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( - x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), + x, y, w, depth, (float)height, ca, a, !print->is_step_done(psWipeTower), bw, &m_wipe_tower_mesh); #else int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( - x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), + x, y, w, depth, (float)height, ca, a, !print->is_step_done(psWipeTower), bw); #endif // ENABLE_OPENGL_ES if (volume_idx_wipe_tower_old != -1) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6ecfa2339..6a17c4ee5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2016,7 +2016,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ "bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", - "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_extra_spacing", + "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3f479030b..6e4f64a99 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1602,6 +1602,7 @@ void TabPrint::build() optgroup->append_single_option_line("wipe_tower_rotation_angle"); optgroup->append_single_option_line("wipe_tower_brim_width"); optgroup->append_single_option_line("wipe_tower_bridging"); + optgroup->append_single_option_line("wipe_tower_cone_angle"); optgroup->append_single_option_line("wipe_tower_extra_spacing"); optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); optgroup->append_single_option_line("single_extruder_multi_material_priming"); From 64cf3579075b99430145946bfaea412d389be0e4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 22 Mar 2023 09:45:30 +0100 Subject: [PATCH 134/201] Wipe tower: fix the cone in combination with 'no_sparse_layers' --- src/libslic3r/GCode/WipeTower.cpp | 10 ++++++++-- src/libslic3r/GCode/WipeTower.hpp | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 5717aa0bb..58af2da48 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1183,7 +1183,9 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() auto supported_rectangle = [this, &writer, spacing](const box_coordinates& wt_box, double feedrate, bool infill_cone) -> Polygon { const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth, m_wipe_tower_cone_angle); - double r = std::tan(Geometry::deg2rad(m_wipe_tower_cone_angle/2.f)) * (m_wipe_tower_height - m_layer_info->z); + double z = m_no_sparse_layers ? (m_current_height + m_layer_info->height) : m_layer_info->z; // the former should actually work in both cases, but let's stay on the safe side (the 2.6.0 is close) + + double r = std::tan(Geometry::deg2rad(m_wipe_tower_cone_angle/2.f)) * (m_wipe_tower_height - z); Vec2f center = (wt_box.lu + wt_box.rd) / 2.; double w = wt_box.lu.y() - wt_box.ld.y(); enum Type { @@ -1313,9 +1315,11 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() // Ask our writer about how much material was consumed. // Skip this in case the layer is sparse and config option to not print sparse layers is enabled. - if (! m_no_sparse_layers || toolchanges_on_layer || first_layer) + if (! m_no_sparse_layers || toolchanges_on_layer || first_layer) { if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + m_current_height += m_layer_info->height; + } return construct_tcr(writer, false, old_tool); } @@ -1380,6 +1384,7 @@ void WipeTower::plan_tower() for (auto& layer : m_plan) layer.depth = 0.f; m_wipe_tower_height = m_plan.empty() ? 0.f : m_plan.back().z; + m_current_height = 0.f; for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { @@ -1471,6 +1476,7 @@ void WipeTower::generate(std::vector> & } m_layer_info = m_plan.begin(); + m_current_height = 0.f; // we don't know which extruder to start with - we'll set it according to the first toolchange for (const auto& layer : m_plan) { diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 15b07f3d8..561123d79 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -364,6 +364,10 @@ private: std::vector m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...)) std::vector::iterator m_layer_info = m_plan.end(); + // This sums height of all extruded layers, not counting the layers which + // will be later removed when the "no_sparse_layers" is used. + float m_current_height = 0.f; + // Stores information about used filament length per extruder: std::vector m_used_filament_length; From 6170783f7c2b60e40d7c35a8c29cbef550634ffa Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Mar 2023 13:49:03 +0100 Subject: [PATCH 135/201] Wipe tower: Increased maximum of wipe_tower_extra_spacing to 300 % --- src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 68e27f4d1..3255c015b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3166,7 +3166,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("%"); def->mode = comExpert; def->min = 100.; - def->max = 200.; + def->max = 300.; def->set_default_value(new ConfigOptionPercent(100.)); def = this->add("wipe_into_infill", coBool); From 30458846405061e4b6334470a30d6b09a7631aba Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 23 Mar 2023 15:00:36 +0100 Subject: [PATCH 136/201] PlaceholderParser: Reduced code duplicity. --- src/libslic3r/PlaceholderParser.cpp | 901 +++++++++++++--------------- 1 file changed, 419 insertions(+), 482 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 55e7235eb..a26841082 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -170,7 +170,7 @@ namespace client template struct OptWithPos { OptWithPos() {} - OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range) : opt(opt), it_range(it_range) {} + OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range, bool writable = false) : opt(opt), it_range(it_range), writable(writable) {} ConfigOptionConstPtr opt { nullptr }; bool writable { false }; // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. @@ -720,9 +720,14 @@ namespace client } struct MyContext : public ConfigOptionResolver { + // Config provided as a parameter to PlaceholderParser invocation, overriding PlaceholderParser stored config. const DynamicConfig *external_config = nullptr; + // Config stored inside PlaceholderParser. const DynamicConfig *config = nullptr; + // Config provided as a parameter to PlaceholderParser invocation, evaluated after the two configs above. const DynamicConfig *config_override = nullptr; + // Config provided as a parameter to PlaceholderParser invocation, containing variables that will be read out + // and processed by the PlaceholderParser callee. mutable DynamicConfig *config_outputs = nullptr; // Local variables, read / write mutable DynamicConfig config_local; @@ -737,6 +742,7 @@ namespace client // Table to translate symbol tag to a human readable error message. static std::map tag_to_error_message; + // Should the parser consider the parsed string to be a macro or a boolean expression? static bool evaluate_full_macro(const MyContext *ctx) { return ! ctx->just_boolean_expression; } const ConfigOption* optptr(const t_config_option_key &opt_key) const override @@ -762,13 +768,13 @@ namespace client out = this->config_local.optptr(opt_key); return out; } - void store_new_variable(const std::string &opt_key, ConfigOption *opt, bool global_variable) { - assert(opt != nullptr); + void store_new_variable(const std::string &opt_key, std::unique_ptr &&opt, bool global_variable) { + assert(opt); if (global_variable) { assert(this->context_data != nullptr && this->context_data->global_config); - this->context_data->global_config->set_key_value(opt_key, opt); + this->context_data->global_config->set_key_value(opt_key, opt.release()); } else - this->config_local.set_key_value(opt_key ,opt); + this->config_local.set_key_value(opt_key, opt.release()); } template @@ -844,9 +850,10 @@ namespace client boost::iterator_range &opt_key, OptWithPos &output) { - const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); + const std::string key{ opt_key.begin(), opt_key.end() }; + const ConfigOption *opt = ctx->resolve_symbol(key); if (opt == nullptr) { - opt = ctx->resolve_output_symbol(std::string(opt_key.begin(), opt_key.end())); + opt = ctx->resolve_output_symbol(key); if (opt == nullptr) ctx->throw_exception("Not a variable name", opt_key); output.writable = true; @@ -872,83 +879,233 @@ namespace client output.it_range.end() = it_end; } + // Evaluating a scalar variable into expr, + // all possible ConfigOption types are supported. + template + static void scalar_variable_to_expr( + const MyContext *ctx, + OptWithPos &opt, + expr &output) + { + assert(opt.opt->is_scalar()); + + switch (opt.opt->type()) { + case coFloat: output.set_d(opt.opt->getFloat()); break; + case coInt: output.set_i(opt.opt->getInt()); break; + case coString: output.set_s(static_cast(opt.opt)->value); break; + case coPercent: output.set_d(opt.opt->getFloat()); break; + case coEnum: + case coPoint: output.set_s(opt.opt->serialize()); break; + case coBool: output.set_b(opt.opt->getBool()); break; + case coFloatOrPercent: + { + std::string opt_key(opt.it_range.begin(), opt.it_range.end()); + if (boost::ends_with(opt_key, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); + } else if (! static_cast(opt.opt)->percent) { + // Not a percent, just return the value. + output.set_d(opt.opt->getFloat()); + } else { + // Resolve dependencies using the "ratio_over" link to a parent value. + const ConfigOptionDef *opt_def = print_config_def.get(opt_key); + assert(opt_def != nullptr); + double v = opt.opt->getFloat() * 0.01; // percent to ratio + for (;;) { + const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); + if (opt_parent == nullptr) + ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); + if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + assert(opt_parent->type() == coFloatOrPercent); + v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); + break; + } + if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { + v *= opt_parent->getFloat(); + if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) + break; + v *= 0.01; // percent to ratio + } + // Continue one level up in the "ratio_over" hierarchy. + opt_def = print_config_def.get(opt_def->ratio_over); + assert(opt_def != nullptr); + } + output.set_d(v); + } + break; + } + default: + ctx->throw_exception("Unsupported scalar variable type", opt.it_range); + } + } + + // Evaluating one element of a vector variable. + // all possible ConfigOption types are supported. + template + static void vector_element_to_expr( + const MyContext *ctx, + OptWithPos &opt, + expr &output) + { + assert(opt.opt->is_vector()); + if (! opt.has_index()) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + const ConfigOptionVectorBase* vec = static_cast(opt.opt); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index); + switch (opt.opt->type()) { + case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; + case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; + case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; + case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; + //case coEnums: output.set_s(opt.opt->vserialize()[idx]); break; + default: + ctx->throw_exception("Unsupported vector variable type", opt.it_range); + } + } + + template + static void check_writable(const MyContext *ctx, OptWithPos &opt) { + if (! opt.writable) + ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + } + + template + static void check_numeric(const expr ¶m) { + if (! param.numeric_type()) + param.throw_exception("Right side is not a numeric expression"); + }; + + template + static size_t evaluate_count(const expr &expr_count) { + if (expr_count.type() != expr::TYPE_INT) + expr_count.throw_exception("Expected number of elements to fill a vector with."); + int count = expr_count.i(); + if (count < 0) + expr_count.throw_exception("Negative number of elements specified."); + return size_t(count); + }; + + template + static void scalar_variable_assign_scalar( + const MyContext *ctx, + OptWithPos &lhs, + const expr &rhs) + { + assert(lhs.opt->is_scalar()); + check_writable(ctx, lhs); + ConfigOption *wropt = const_cast(lhs.opt); + switch (wropt->type()) { + case coFloat: + check_numeric(rhs); + static_cast(wropt)->value = rhs.as_d(); + break; + case coInt: + check_numeric(rhs); + static_cast(wropt)->value = rhs.as_i(); + break; + case coString: + static_cast(wropt)->value = rhs.to_string(); + break; + case coPercent: + check_numeric(rhs); + static_cast(wropt)->value = rhs.as_d(); + break; + case coBool: + if (rhs.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", rhs.it_range); + static_cast(wropt)->value = rhs.b(); + break; + default: + ctx->throw_exception("Unsupported output scalar variable type", lhs.it_range); + } + } + + template + static void vector_variable_element_assign_scalar( + const MyContext *ctx, + OptWithPos &lhs, + const expr &rhs) + { + assert(lhs.opt->is_vector()); + check_writable(ctx, lhs); + if (! lhs.has_index()) + ctx->throw_exception("Referencing an output vector variable when scalar is expected", lhs.it_range); + ConfigOptionVectorBase *vec = const_cast(static_cast(lhs.opt)); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", lhs.it_range); + if (lhs.index >= int(vec->size())) + ctx->throw_exception("Index out of range", lhs.it_range); + switch (lhs.opt->type()) { + case coFloats: + check_numeric(rhs); + static_cast(vec)->values[lhs.index] = rhs.as_d(); + break; + case coInts: + check_numeric(rhs); + static_cast(vec)->values[lhs.index] = rhs.as_i(); + break; + case coStrings: + static_cast(vec)->values[lhs.index] = rhs.to_string(); + break; + case coPercents: + check_numeric(rhs); + static_cast(vec)->values[lhs.index] = rhs.as_d(); + break; + case coBools: + if (rhs.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", rhs.it_range); + static_cast(vec)->values[lhs.index] = rhs.b(); + break; + default: + ctx->throw_exception("Unsupported output vector variable type", lhs.it_range); + } + } + + template + static void vector_variable_assign_expr_with_count( + const MyContext *ctx, + OptWithPos &lhs, + const expr &rhs_count, + const expr &rhs_value) + { + size_t count = evaluate_count(rhs_count); + auto *opt = const_cast(lhs.opt); + switch (lhs.opt->type()) { + case coFloats: + check_numeric(rhs_value); + static_cast(opt)->values.assign(count, rhs_value.as_d()); + break; + case coInts: + check_numeric(rhs_value); + static_cast(opt)->values.assign(count, rhs_value.as_i()); + break; + case coStrings: + static_cast(opt)->values.assign(count, rhs_value.to_string()); + break; + case coBools: + if (rhs_value.type() != expr::TYPE_BOOL) + rhs_value.throw_exception("Right side is not a boolean expression"); + static_cast(opt)->values.assign(count, rhs_value.b()); + break; + default: assert(false); + } + } + template static void variable_value( const MyContext *ctx, OptWithPos &opt, expr &output) { - if (opt.opt->is_vector()) { - if (! opt.has_index()) - ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); - const ConfigOptionVectorBase *vec = static_cast(opt.opt); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index); - switch (opt.opt->type()) { - case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; - case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; - case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; - case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; - //case coEnums: output.set_s(opt.opt->vserialize()[idx]); break; - default: - ctx->throw_exception("Unknown vector variable type", opt.it_range); - } - } else { - assert(opt.opt->is_scalar()); - switch (opt.opt->type()) { - case coFloat: output.set_d(opt.opt->getFloat()); break; - case coInt: output.set_i(opt.opt->getInt()); break; - case coString: output.set_s(static_cast(opt.opt)->value); break; - case coPercent: output.set_d(opt.opt->getFloat()); break; - case coEnum: - case coPoint: output.set_s(opt.opt->serialize()); break; - case coBool: output.set_b(opt.opt->getBool()); break; - case coFloatOrPercent: - { - std::string opt_key(opt.it_range.begin(), opt.it_range.end()); - if (boost::ends_with(opt_key, "extrusion_width")) { - // Extrusion width supports defaults and a complex graph of dependencies. - output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); - } else if (! static_cast(opt.opt)->percent) { - // Not a percent, just return the value. - output.set_d(opt.opt->getFloat()); - } else { - // Resolve dependencies using the "ratio_over" link to a parent value. - const ConfigOptionDef *opt_def = print_config_def.get(opt_key); - assert(opt_def != nullptr); - double v = opt.opt->getFloat() * 0.01; // percent to ratio - for (;;) { - const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); - if (opt_parent == nullptr) - ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); - if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { - // Extrusion width supports defaults and a complex graph of dependencies. - assert(opt_parent->type() == coFloatOrPercent); - v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); - break; - } - if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { - v *= opt_parent->getFloat(); - if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) - break; - v *= 0.01; // percent to ratio - } - // Continue one level up in the "ratio_over" hierarchy. - opt_def = print_config_def.get(opt_def->ratio_over); - assert(opt_def != nullptr); - } - output.set_d(v); - } - break; - } - default: - ctx->throw_exception("Unknown scalar variable type", opt.it_range); - } - } - + if (opt.opt->is_vector()) + vector_element_to_expr(ctx, opt, output); + else + scalar_variable_to_expr(ctx, opt, output); output.it_range = opt.it_range; } @@ -974,6 +1131,7 @@ namespace client output.it_range = opt.it_range; } + // Reference to an existing symbol, or a name of a new symbol. template struct NewOldVariable { std::string name; @@ -1010,240 +1168,148 @@ namespace client out.it_range = it_range; } - template - static void new_scalar_variable( - const MyContext *ctx, - bool global_variable, - NewOldVariable &output_variable, - const expr ¶m) - { - auto check_numeric = [](const expr ¶m) { - if (! param.numeric_type()) - param.throw_exception("Right side is not a numeric expression"); - }; - if (output_variable.opt) { - if (output_variable.opt->is_vector()) - param.throw_exception("Cannot assign a scalar value to a vector variable."); - switch (output_variable.opt->type()) { - case coFloat: - check_numeric(param); - static_cast(output_variable.opt)->value = param.as_d(); - break; - case coInt: - check_numeric(param); - static_cast(output_variable.opt)->value = param.as_i(); - break; - case coString: - static_cast(output_variable.opt)->value = param.to_string(); - break; - case coBool: - if (param.type() != expr::TYPE_BOOL) - param.throw_exception("Right side is not a boolean expression"); - static_cast(output_variable.opt)->value = param.b(); - break; - default: assert(false); - } - } else { - switch (param.type()) { - case expr::TYPE_BOOL: output_variable.opt = new ConfigOptionBool(param.b()); break; - case expr::TYPE_INT: output_variable.opt = new ConfigOptionInt(param.i()); break; - case expr::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloat(param.d()); break; - case expr::TYPE_STRING: output_variable.opt = new ConfigOptionString(param.s()); break; - default: assert(false); - } - const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); - } - } - - template - static void check_writable(const MyContext *ctx, OptWithPos &opt) { - if (! opt.writable) - ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); - } - // Decoding a scalar variable symbol "opt", assigning it a value of "param". template - static void assign_scalar_variable( + static void scalar_variable_assign_scalar_expression( const MyContext *ctx, OptWithPos &opt, - expr ¶m) + const expr ¶m) { check_writable(ctx, opt); - auto check_numeric = [](const expr ¶m) { - if (! param.numeric_type()) - param.throw_exception("Right side is not a numeric expression"); - }; - if (opt.opt->is_vector()) { - if (! opt.has_index()) - ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); - ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - if (opt.index >= int(vec->size())) - ctx->throw_exception("Index out of range", opt.it_range); - switch (opt.opt->type()) { - case coFloats: - check_numeric(param); - static_cast(vec)->values[opt.index] = param.as_d(); - break; - case coInts: - check_numeric(param); - static_cast(vec)->values[opt.index] = param.as_i(); - break; - case coStrings: - static_cast(vec)->values[opt.index] = param.to_string(); - break; - case coPercents: - check_numeric(param); - static_cast(vec)->values[opt.index] = param.as_d(); - break; - case coBools: - if (param.type() != expr::TYPE_BOOL) - ctx->throw_exception("Right side is not a boolean expression", param.it_range); - static_cast(vec)->values[opt.index] = param.b(); - break; - default: - ctx->throw_exception("Unsupported output vector variable type", opt.it_range); - } - } else { - assert(opt.opt->is_scalar()); - ConfigOption *wropt = const_cast(opt.opt); - switch (wropt->type()) { - case coFloat: - check_numeric(param); - static_cast(wropt)->value = param.as_d(); - break; - case coInt: - check_numeric(param); - static_cast(wropt)->value = param.as_i(); - break; - case coString: - static_cast(wropt)->value = param.to_string(); - break; - case coPercent: - check_numeric(param); - static_cast(wropt)->value = param.as_d(); - break; - case coBool: - if (param.type() != expr::TYPE_BOOL) - ctx->throw_exception("Right side is not a boolean expression", param.it_range); - static_cast(wropt)->value = param.b(); - break; - default: - ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); - } - } + if (opt.opt->is_vector()) + vector_variable_element_assign_scalar(ctx, opt, param); + else + scalar_variable_assign_scalar(ctx, opt, param); } template - static void new_vector_variable_array( + static void scalar_variable_new_from_scalar_expression( const MyContext *ctx, bool global_variable, - NewOldVariable &output_variable, - const expr &expr_count, - const expr &expr_value) + NewOldVariable &lhs, + const expr &rhs) { - auto check_numeric = [](const expr ¶m) { - if (! param.numeric_type()) - param.throw_exception("Right side is not a numeric expression"); - }; - auto evaluate_count = [](const expr &expr_count) -> size_t { - if (expr_count.type() != expr::TYPE_INT) - expr_count.throw_exception("Expected number of elements to fill a vector with."); - int count = expr_count.i(); - if (count < 0) - expr_count.throw_exception("Negative number of elements specified."); - return size_t(count); - }; - if (output_variable.opt) { - if (output_variable.opt->is_scalar()) - expr_value.throw_exception("Cannot assign a vector value to a scalar variable."); - size_t count = evaluate_count(expr_count); - switch (output_variable.opt->type()) { - case coFloats: - check_numeric(expr_value); - static_cast(output_variable.opt)->values.assign(count, expr_value.as_d()); - break; - case coInts: - check_numeric(expr_value); - static_cast(output_variable.opt)->values.assign(count, expr_value.as_i()); - break; - case coStrings: - static_cast(output_variable.opt)->values.assign(count, expr_value.to_string()); - break; - case coBools: - if (expr_value.type() != expr::TYPE_BOOL) - expr_value.throw_exception("Right side is not a boolean expression"); - static_cast(output_variable.opt)->values.assign(count, expr_value.b()); - break; - default: assert(false); - } + if (lhs.opt) { + if (lhs.opt->is_vector()) + rhs.throw_exception("Cannot assign a scalar value to a vector variable."); + OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; + scalar_variable_assign_scalar(ctx, lhs_opt, rhs); } else { - size_t count = evaluate_count(expr_count); - switch (expr_value.type()) { - case expr::TYPE_BOOL: output_variable.opt = new ConfigOptionBools(count, expr_value.b()); break; - case expr::TYPE_INT: output_variable.opt = new ConfigOptionInts(count, expr_value.i()); break; - case expr::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloats(count, expr_value.d()); break; - case expr::TYPE_STRING: output_variable.opt = new ConfigOptionStrings(count, expr_value.s()); break; + std::unique_ptr opt_new; + switch (rhs.type()) { + case expr::TYPE_BOOL: opt_new = std::make_unique(rhs.b()); break; + case expr::TYPE_INT: opt_new = std::make_unique(rhs.i()); break; + case expr::TYPE_DOUBLE: opt_new = std::make_unique(rhs.d()); break; + case expr::TYPE_STRING: opt_new = std::make_unique(rhs.s()); break; default: assert(false); } - const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); + const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); } } template - static void assign_vector_variable_array( + static void vector_variable_new_from_array( + const MyContext *ctx, + bool global_variable, + NewOldVariable &lhs, + const expr &rhs_count, + const expr &rhs_value) + { + if (lhs.opt) { + if (lhs.opt->is_scalar()) + rhs_value.throw_exception("Cannot assign a vector value to a scalar variable."); + OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; + vector_variable_assign_expr_with_count(ctx, lhs_opt, rhs_count, rhs_value); + } else { + size_t count = evaluate_count(rhs_count); + std::unique_ptr opt_new; + switch (rhs_value.type()) { + case expr::TYPE_BOOL: opt_new = std::make_unique(count, rhs_value.b()); break; + case expr::TYPE_INT: opt_new = std::make_unique(count, rhs_value.i()); break; + case expr::TYPE_DOUBLE: opt_new = std::make_unique(count, rhs_value.d()); break; + case expr::TYPE_STRING: opt_new = std::make_unique(count, rhs_value.s()); break; + default: assert(false); + } + const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); + } + } + + template + static void vector_variable_assign_array( const MyContext *ctx, OptWithPos &lhs, - const expr &expr_count, - const expr &expr_value) + const expr &rhs_count, + const expr &rhs_value) { check_writable(ctx, lhs); - auto check_numeric = [](const expr ¶m) { - if (! param.numeric_type()) - param.throw_exception("Right side is not a numeric expression"); - }; - auto evaluate_count = [](const expr &expr_count) -> size_t { - if (expr_count.type() != expr::TYPE_INT) - expr_count.throw_exception("Expected number of elements to fill a vector with."); - int count = expr_count.i(); - if (count < 0) - expr_count.throw_exception("Negative number of elements specified."); - return size_t(count); - }; if (lhs.opt->is_scalar()) - expr_value.throw_exception("Cannot assign a vector value to a scalar variable."); - auto *opt = const_cast(lhs.opt); - size_t count = evaluate_count(expr_count); + rhs_value.throw_exception("Cannot assign a vector value to a scalar variable."); + vector_variable_assign_expr_with_count(ctx, lhs, rhs_count, rhs_value); + } + + template + static void fill_vector_from_initializer_list(ConfigOption *opt, const std::vector> &il, RightValueEvaluate rv_eval) { + auto& out = static_cast(opt)->values; + out.clear(); + out.reserve(il.size()); + for (const expr& i : il) + out.emplace_back(rv_eval(i)); + } + + template + static void vector_variable_assign_initializer_list( + const MyContext *ctx, + OptWithPos &lhs, + const std::vector> &il) + { + check_writable(ctx, lhs); + auto check_numeric_vector = [](const std::vector> &il) { + for (auto &i : il) + if (! i.numeric_type()) + i.throw_exception("Right side is not a numeric expression"); + }; + + if (lhs.opt->is_scalar()) + ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); + + ConfigOption *opt = const_cast(lhs.opt); switch (lhs.opt->type()) { case coFloats: - check_numeric(expr_value); - static_cast(opt)->values.assign(count, expr_value.as_d()); + check_numeric_vector(il); + fill_vector_from_initializer_list(opt, il, [](auto &v){ return v.as_d(); }); break; case coInts: - check_numeric(expr_value); - static_cast(opt)->values.assign(count, expr_value.as_i()); + check_numeric_vector(il); + fill_vector_from_initializer_list(opt, il, [](auto &v){ return v.as_i(); }); break; case coStrings: - static_cast(opt)->values.assign(count, expr_value.to_string()); + fill_vector_from_initializer_list(opt, il, [](auto &v){ return v.to_string(); }); break; case coBools: - if (expr_value.type() != expr::TYPE_BOOL) - expr_value.throw_exception("Right side is not a boolean expression"); - static_cast(opt)->values.assign(count, expr_value.b()); + for (auto &i : il) + if (i.type() != expr::TYPE_BOOL) + i.throw_exception("Right side is not a boolean expression"); + fill_vector_from_initializer_list(opt, il, [](auto &v){ return v.b(); }); break; default: assert(false); } } template - static void new_vector_variable_initializer_list( + static void vector_variable_new_from_initializer_list( const MyContext *ctx, bool global_variable, - NewOldVariable &output_variable, + NewOldVariable &lhs, const std::vector> &il) { - if (! output_variable.opt) { + if (lhs.opt) { + // Assign to an existing vector variable. + if (lhs.opt->is_scalar()) + ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); + OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; + vector_variable_assign_initializer_list(ctx, lhs_opt, il); + } else { + // Allocate a new vector variable. // First guesstimate type of the output vector. size_t num_bool = 0; size_t num_int = 0; @@ -1257,186 +1323,53 @@ namespace client case expr::TYPE_STRING: ++ num_string; break; default: assert(false); } + std::unique_ptr opt_new; if (num_string > 0) // Convert everything to strings. - output_variable.opt = new ConfigOptionStrings(); + opt_new = std::make_unique(); else if (num_bool > 0) { if (num_double + num_int > 0) ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range{ il.front().it_range.begin(), il.back().it_range.end() }); - output_variable.opt = new ConfigOptionBools(); - } else + opt_new = std::make_unique(); + } else { // Output is numeric. - output_variable.opt = num_double == 0 ? static_cast(new ConfigOptionInts()) : static_cast(new ConfigOptionFloats()); - const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); - } - - auto check_numeric = [](const std::vector> &il) { - for (auto& i : il) - if (!i.numeric_type()) - i.throw_exception("Right side is not a numeric expression"); - }; - - if (output_variable.opt->is_scalar()) - ctx->throw_exception("Cannot assign a vector value to a scalar variable.", output_variable.it_range); - - switch (output_variable.opt->type()) { - case coFloats: - { - check_numeric(il); - auto &out = static_cast(output_variable.opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto &i : il) - out.emplace_back(i.as_d()); - break; - } - case coInts: - { - check_numeric(il); - auto &out = static_cast(output_variable.opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto& i : il) - out.emplace_back(i.as_i()); - break; - } - case coStrings: - { - auto &out = static_cast(output_variable.opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto &i : il) - out.emplace_back(i.to_string()); - break; - } - case coBools: - { - auto &out = static_cast(output_variable.opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto &i : il) - if (i.type() == expr::TYPE_BOOL) - out.emplace_back(i.b()); - else - i.throw_exception("Right side is not a boolean expression"); - break; - } - default: - assert(false); + if (num_double == 0) + opt_new = std::make_unique(); + else + opt_new = std::make_unique(); + } + OptWithPos lhs_opt{ opt_new.get(), lhs.it_range, true }; + vector_variable_assign_initializer_list(ctx, lhs_opt, il); + const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); } } template - static void assign_vector_variable_initializer_list( - const MyContext *ctx, - OptWithPos &lhs, - const std::vector> &il) + static void copy_vector_variable_to_vector_variable( + const MyContext *ctx, + OptWithPos &lhs, + const OptWithPos &rhs) { check_writable(ctx, lhs); - auto check_numeric = [](const std::vector> &il) { - for (auto &i : il) - if (! i.numeric_type()) - i.throw_exception("Right side is not a numeric expression"); - }; - - if (lhs.opt->is_scalar()) - ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); - - ConfigOption *opt = const_cast(lhs.opt); - switch (lhs.opt->type()) { - case coFloats: - { - check_numeric(il); - auto &out = static_cast(opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto &i : il) - out.emplace_back(i.as_d()); - break; + assert(rhs.opt->is_vector()); + if (! lhs.opt->is_vector()) + ctx->throw_exception("Cannot assign vector to a scalar", lhs.it_range); + if (lhs.opt->type() != rhs.opt->type()) { + // Vector types are not compatible. + switch (lhs.opt->type()) { + case coFloats: + ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range); + case coInts: + ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range); + case coStrings: + ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range); + case coBools: + ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range); + default: + ctx->throw_exception("Left hand side / right hand side vectors are not compatible.", lhs.it_range); + } } - case coInts: - { - check_numeric(il); - auto &out = static_cast(opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto& i : il) - out.emplace_back(i.as_i()); - break; - } - case coStrings: - { - auto &out = static_cast(opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto &i : il) - out.emplace_back(i.to_string()); - break; - } - case coBools: - { - auto &out = static_cast(opt)->values; - out.clear(); - out.reserve(il.size()); - for (auto &i : il) - if (i.type() == expr::TYPE_BOOL) - out.emplace_back(i.b()); - else - i.throw_exception("Right side is not a boolean expression"); - break; - } - default: - assert(false); - } - } - - template - static bool new_vector_variable_copy( - const MyContext *ctx, - bool global_variable, - NewOldVariable &output_variable, - const OptWithPos &src_variable) - { - if (! is_vector_variable_reference(src_variable)) - // Skip parsing this branch, bactrack. - return false; - - if (! output_variable.opt) { - if (one_of(src_variable.opt->type(), { coFloats, coInts, coStrings, coBools })) - output_variable.opt = src_variable.opt->clone(); - else if (src_variable.opt->type() == coPercents) - output_variable.opt = new ConfigOptionFloats(static_cast(src_variable.opt)->values); - else - ctx->throw_exception("Duplicating this vector variable is not supported", src_variable.it_range); - const_cast(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable); - } - - switch (output_variable.opt->type()) { - case coFloats: - if (output_variable.opt->type() != coFloats) - ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); - static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; - break; - case coInts: - if (output_variable.opt->type() != coInts) - ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); - static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; - break; - case coStrings: - if (output_variable.opt->type() != coStrings) - ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); - static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; - break; - case coBools: - if (output_variable.opt->type() != coBools) - ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", boost::iterator_range{ output_variable.it_range.begin(), src_variable.it_range.end() }); - static_cast(output_variable.opt)->values = static_cast(src_variable.opt)->values; - break; - default: - assert(false); - } - // Continue parsing. - return true; + const_cast(lhs.opt)->set(rhs.opt); } template @@ -1445,49 +1378,53 @@ namespace client } template - static bool assign_vector_variable_copy( + static bool vector_variable_new_from_copy( const MyContext *ctx, - OptWithPos &lhs, - const OptWithPos &src_variable) + bool global_variable, + NewOldVariable &lhs, + const OptWithPos &rhs) { - if (! is_vector_variable_reference(src_variable)) + if (is_vector_variable_reference(rhs)) { + if (lhs.opt) { + OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; + copy_vector_variable_to_vector_variable(ctx, lhs_opt, rhs); + } else { + // Clone the vector variable. + std::unique_ptr opt_new; + if (one_of(rhs.opt->type(), { coFloats, coInts, coStrings, coBools })) + opt_new = std::unique_ptr(rhs.opt->clone()); + else if (rhs.opt->type() == coPercents) + opt_new = std::make_unique(static_cast(rhs.opt)->values); + else + ctx->throw_exception("Duplicating this type of vector variable is not supported", rhs.it_range); + const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); + } + // Continue parsing. + return true; + } else { // Skip parsing this branch, bactrack. return false; - - check_writable(ctx, lhs); - - auto *opt = const_cast(lhs.opt); - switch (lhs.opt->type()) { - case coFloats: - if (lhs.opt->type() != coFloats) - ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range); - static_cast(opt)->values = static_cast(src_variable.opt)->values; - break; - case coInts: - if (lhs.opt->type() != coInts) - ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range); - static_cast(opt)->values = static_cast(src_variable.opt)->values; - break; - case coStrings: - if (lhs.opt->type() != coStrings) - ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range); - static_cast(opt)->values = static_cast(src_variable.opt)->values; - break; - case coBools: - if (lhs.opt->type() != coBools) - ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range); - static_cast(opt)->values = static_cast(src_variable.opt)->values; - break; - default: - assert(false); } - - // Continue parsing. - return true; } template - static void new_vector_variable_initializer_list_append(std::vector> &list, expr &expr) + static bool vector_variable_assign_copy( + const MyContext *ctx, + OptWithPos &lhs, + const OptWithPos &rhs) + { + if (is_vector_variable_reference(rhs)) { + copy_vector_variable_to_vector_variable(ctx, lhs, rhs); + // Continue parsing. + return true; + } else { + // Skip parsing this branch, bactrack. + return false; + } + } + + template + static void initializer_list_append(std::vector> &list, expr &expr) { list.emplace_back(std::move(expr)); } @@ -1910,36 +1847,36 @@ namespace client variable_reference(_r1)[_a = _1] >> '=' > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. (lit('(') > new_variable_initializer_list(_r1) > ')') - [px::bind(&MyContext::assign_vector_variable_initializer_list, _r1, _a, _1)] + [px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. | variable_reference(_r1) - [px::ref(qi::_pass) = px::bind(&MyContext::assign_vector_variable_copy, _r1, _a, _1)] + [px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_assign_copy, _r1, _a, _1)] // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. | conditional_expression(_r1) - [px::bind(&MyContext::assign_scalar_variable, _r1, _a, _1)] + [px::bind(&MyContext::scalar_variable_assign_scalar_expression, _r1, _a, _1)] | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") - [px::bind(&MyContext::assign_vector_variable_array, _r1, _a, _1, _2)] + [px::bind(&MyContext::vector_variable_assign_array, _r1, _a, _1, _2)] ); new_variable_statement = (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable, _r1, _a, _1, _b)] > lit('=') > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. (lit('(') > new_variable_initializer_list(_r1) > ')') - [px::bind(&MyContext::new_vector_variable_initializer_list, _r1, _a, _b, _1)] + [px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. | variable_reference(_r1) - [px::ref(qi::_pass) = px::bind(&MyContext::new_vector_variable_copy, _r1, _a, _b, _1)] + [px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)] // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. | conditional_expression(_r1) - [px::bind(&MyContext::new_scalar_variable, _r1, _a, _b, _1)] + [px::bind(&MyContext::scalar_variable_new_from_scalar_expression, _r1, _a, _b, _1)] | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") - [px::bind(&MyContext::new_vector_variable_array, _r1, _a, _b, _1, _2)] + [px::bind(&MyContext::vector_variable_new_from_array, _r1, _a, _b, _1, _2)] ); new_variable_initializer_list = - conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append, _val, _1)] >> - *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append, _val, _1)]); + conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)] >> + *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]); struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) From 929a0eba2cfe6603f036e13083d5f349a0ccb0b7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 23 Mar 2023 17:08:56 +0100 Subject: [PATCH 137/201] PlaceholderParser: replaced the new "array" operator with "repeat", simplified the parser a bit more. --- src/libslic3r/PlaceholderParser.cpp | 127 +++++++++----------- tests/libslic3r/test_placeholder_parser.cpp | 18 +-- 2 files changed, 68 insertions(+), 77 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index a26841082..29d35ac2f 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1263,15 +1263,24 @@ namespace client const std::vector> &il) { check_writable(ctx, lhs); + + if (lhs.opt->is_scalar()) { + if (il.size() == 1) + // scalar_var = ( scalar ) + scalar_variable_assign_scalar_expression(ctx, lhs, il.front()); + else + // scalar_var = () + // or + // scalar_var = ( scalar, scalar, ... ) + ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); + } + auto check_numeric_vector = [](const std::vector> &il) { for (auto &i : il) if (! i.numeric_type()) i.throw_exception("Right side is not a numeric expression"); }; - if (lhs.opt->is_scalar()) - ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); - ConfigOption *opt = const_cast(lhs.opt); switch (lhs.opt->type()) { case coFloats: @@ -1304,11 +1313,11 @@ namespace client { if (lhs.opt) { // Assign to an existing vector variable. - if (lhs.opt->is_scalar()) - ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; vector_variable_assign_initializer_list(ctx, lhs_opt, il); } else { + if (il.empty()) + ctx->throw_exception("Cannot create vector variable from an empty initializer list, because its type cannot be deduced.", lhs.it_range); // Allocate a new vector variable. // First guesstimate type of the output vector. size_t num_bool = 0; @@ -1344,6 +1353,17 @@ namespace client } } + template + static bool is_vector_variable_reference(const OptWithPos &var) { + return ! var.has_index() && var.opt->is_vector(); + } + + // Called when checking whether the NewOldVariable could be assigned a vectir right hand side. + template + static bool could_be_vector_variable_reference(const NewOldVariable &var) { + return var.opt == nullptr || var.opt->is_vector(); + } + template static void copy_vector_variable_to_vector_variable( const MyContext *ctx, @@ -1351,9 +1371,9 @@ namespace client const OptWithPos &rhs) { check_writable(ctx, lhs); - assert(rhs.opt->is_vector()); - if (! lhs.opt->is_vector()) - ctx->throw_exception("Cannot assign vector to a scalar", lhs.it_range); + assert(lhs.opt->is_vector()); + if (rhs.has_index() || ! rhs.opt->is_vector()) + ctx->throw_exception("Cannot assign scalar to a vector", lhs.it_range); if (lhs.opt->type() != rhs.opt->type()) { // Vector types are not compatible. switch (lhs.opt->type()) { @@ -1372,11 +1392,6 @@ namespace client const_cast(lhs.opt)->set(rhs.opt); } - template - static bool is_vector_variable_reference(const OptWithPos &var) { - return ! var.has_index() && var.opt->is_vector(); - } - template static bool vector_variable_new_from_copy( const MyContext *ctx, @@ -1384,43 +1399,26 @@ namespace client NewOldVariable &lhs, const OptWithPos &rhs) { - if (is_vector_variable_reference(rhs)) { - if (lhs.opt) { - OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; - copy_vector_variable_to_vector_variable(ctx, lhs_opt, rhs); - } else { - // Clone the vector variable. - std::unique_ptr opt_new; - if (one_of(rhs.opt->type(), { coFloats, coInts, coStrings, coBools })) - opt_new = std::unique_ptr(rhs.opt->clone()); - else if (rhs.opt->type() == coPercents) - opt_new = std::make_unique(static_cast(rhs.opt)->values); - else - ctx->throw_exception("Duplicating this type of vector variable is not supported", rhs.it_range); - const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); - } - // Continue parsing. - return true; + if (lhs.opt) { + assert(lhs.opt->is_vector()); + OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; + copy_vector_variable_to_vector_variable(ctx, lhs_opt, rhs); } else { - // Skip parsing this branch, bactrack. - return false; - } - } - - template - static bool vector_variable_assign_copy( - const MyContext *ctx, - OptWithPos &lhs, - const OptWithPos &rhs) - { - if (is_vector_variable_reference(rhs)) { - copy_vector_variable_to_vector_variable(ctx, lhs, rhs); - // Continue parsing. - return true; - } else { - // Skip parsing this branch, bactrack. - return false; + if (rhs.has_index() || ! rhs.opt->is_vector()) + // Stop parsing, let the other rules resolve this case. + return false; + // Clone the vector variable. + std::unique_ptr opt_new; + if (one_of(rhs.opt->type(), { coFloats, coInts, coStrings, coBools })) + opt_new = std::unique_ptr(rhs.opt->clone()); + else if (rhs.opt->type() == coPercents) + opt_new = std::make_unique(static_cast(rhs.opt)->values); + else + ctx->throw_exception("Duplicating this type of vector variable is not supported", rhs.it_range); + const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); } + // Continue parsing. + return true; } template @@ -1572,12 +1570,12 @@ namespace client // Table to translate symbol tag to a human readable error message. std::map MyContext::tag_to_error_message = { - { "array", "Unknown syntax error" }, { "eoi", "Unknown syntax error" }, { "start", "Unknown syntax error" }, { "text", "Invalid text." }, { "text_block", "Invalid text block." }, { "macro", "Invalid macro." }, + { "repeat", "Unknown syntax error" }, { "if_else_output", "Not an {if}{else}{endif} macro." }, { "switch_output", "Not a {switch} macro." }, { "legacy_variable_expansion", "Expecting a legacy variable expansion format" }, @@ -1594,7 +1592,6 @@ namespace client { "optional_parameter", "Expecting a closing brace or an optional parameter." }, { "one_of_list", "Expecting a list of string patterns (simple text or rexep)" }, { "variable_reference", "Expecting a variable reference."}, - { "is_nil_test", "Expecting a scalar variable reference."}, { "variable", "Expecting a variable name."}, { "regular_expression", "Expecting a regular expression."} }; @@ -1846,37 +1843,35 @@ namespace client assignment_statement = variable_reference(_r1)[_a = _1] >> '=' > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. - (lit('(') > new_variable_initializer_list(_r1) > ')') + (lit('(') > initializer_list(_r1) > ')') [px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. - | variable_reference(_r1) - [px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_assign_copy, _r1, _a, _1)] + | eps(px::bind(&MyContext::is_vector_variable_reference, _a)) >> + variable_reference(_r1)[px::bind(&MyContext::copy_vector_variable_to_vector_variable, _r1, _a, _1)] // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. | conditional_expression(_r1) [px::bind(&MyContext::scalar_variable_assign_scalar_expression, _r1, _a, _1)] - | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") + | (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") [px::bind(&MyContext::vector_variable_assign_array, _r1, _a, _1, _2)] ); new_variable_statement = (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable, _r1, _a, _1, _b)] > lit('=') > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. - (lit('(') > new_variable_initializer_list(_r1) > ')') + (lit('(') > initializer_list(_r1) > ')') [px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. - | variable_reference(_r1) - [px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)] + | eps(px::bind(&MyContext::could_be_vector_variable_reference, _b)) >> + variable_reference(_r1)[px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)] // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. | conditional_expression(_r1) [px::bind(&MyContext::scalar_variable_new_from_scalar_expression, _r1, _a, _b, _1)] - | (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") + | (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") [px::bind(&MyContext::vector_variable_new_from_array, _r1, _a, _b, _1, _2)] ); - new_variable_initializer_list = - conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)] >> - *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]); + initializer_list = *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]); struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) @@ -1920,7 +1915,7 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] - | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ] + | (kw["is_nil"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_nil_test, _r1, _1, _val)] | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] | (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] @@ -1961,9 +1956,6 @@ namespace client ); optional_parameter.name("optional_parameter"); - is_nil_test = variable_reference(_r1)[px::bind(&MyContext::is_nil_test, _r1, _1, _val)]; - is_nil_test.name("is_nil test"); - variable_reference = variable(_r1)[_a=_1] >> ( @@ -1981,7 +1973,6 @@ namespace client keywords.add ("and") - ("array") ("digits") ("zdigits") ("if") @@ -1998,6 +1989,7 @@ namespace client ("min") ("max") ("random") + ("repeat") ("round") ("not") ("one_of") @@ -2030,7 +2022,6 @@ namespace client debug(optional_parameter); debug(variable_reference); debug(variable); - debug(is_nil_test); debug(regular_expression); } } @@ -2089,7 +2080,7 @@ namespace client qi::rule>, spirit_encoding::space_type> assignment_statement; // Allocating new local or global variables. qi::rule>, spirit_encoding::space_type> new_variable_statement; - qi::rule>(const MyContext*), spirit_encoding::space_type> new_variable_initializer_list; + qi::rule>(const MyContext*), spirit_encoding::space_type> initializer_list; // qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; qi::symbols keywords; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index ee1461baf..48fe43117 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -186,12 +186,12 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { SECTION("create a string global variable and redefine it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{global mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); } SECTION("create a bool global variable and redefine it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{global mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); } - SECTION("create an ints local variable with array()") { REQUIRE(parser.process("{local myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); } - SECTION("create a strings local variable array()") { REQUIRE(parser.process("{local mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); } - SECTION("create a bools local variable array()") { REQUIRE(parser.process("{local mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); } - SECTION("create an ints global variable array()") { REQUIRE(parser.process("{global myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); } - SECTION("create a strings global variable array()") { REQUIRE(parser.process("{global mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); } - SECTION("create a bools global variable array()") { REQUIRE(parser.process("{global mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); } + SECTION("create an ints local variable with repeat()") { REQUIRE(parser.process("{local myint = repeat(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); } + SECTION("create a strings local variable with repeat()") { REQUIRE(parser.process("{local mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); } + SECTION("create a bools local variable with repeat()") { REQUIRE(parser.process("{local mybool = repeat(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); } + SECTION("create an ints global variable with repeat()") { REQUIRE(parser.process("{global myint = repeat(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); } + SECTION("create a strings global variable with repeat()") { REQUIRE(parser.process("{global mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); } + SECTION("create a bools global variable with repeat()") { REQUIRE(parser.process("{global mybool = repeat(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); } SECTION("create an ints local variable with initializer list") { REQUIRE(parser.process("{local myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, nullptr) == "24"); } SECTION("create a strings local variable with initializer list") { REQUIRE(parser.process("{local mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); } @@ -208,17 +208,17 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { SECTION("create a bools global variable by a copy") { REQUIRE(parser.process("{global mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, &context_with_global_dict) == "true"); } SECTION("create an ints local variable by a copy and overwrite it") { - REQUIRE(parser.process("{local myint = temperature}{myint = array(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24"); + REQUIRE(parser.process("{local myint = temperature}{myint = repeat(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24"); REQUIRE(parser.process("{local myint = temperature}{myint = (2*3, 4*6)}{myint[1]}", 0, &config, nullptr, nullptr) == "24"); REQUIRE(parser.process("{local myint = temperature}{myint = (1)}{myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357"); } SECTION("create a strings local variable by a copy and overwrite it") { - REQUIRE(parser.process("{local mystr = filament_notes}{mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine"); + REQUIRE(parser.process("{local mystr = filament_notes}{mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine"); REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr[1]}", 0, &config, nullptr, nullptr) == "mineonlymine"); REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes"); } SECTION("create a bools local variable by a copy and overwrite it") { - REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = array(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true"); + REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = repeat(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true"); REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, true)}{mybool[1]}", 0, &config, nullptr, nullptr) == "true"); REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, false)}{mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); } From 9cb50e6586c1f339b84c29fbe078b3521a0405b9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 23 Mar 2023 17:51:37 +0100 Subject: [PATCH 138/201] PlaceholderParser: added size() and empty() functions over vector variables. --- src/libslic3r/PlaceholderParser.cpp | 42 ++++++++++++++++++--- tests/libslic3r/test_placeholder_parser.cpp | 5 +++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 29d35ac2f..94a1259b6 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1427,6 +1427,30 @@ namespace client list.emplace_back(std::move(expr)); } + template + static void is_vector_empty( + const MyContext *ctx, + OptWithPos &opt, + expr &out) + { + if (opt.has_index() || ! opt.opt->is_vector()) + ctx->throw_exception("parameter of empty() is not a vector variable", opt.it_range); + out.set_b(static_cast(opt.opt)->size() == 0); + out.it_range = opt.it_range; + } + + template + static void vector_size( + const MyContext *ctx, + OptWithPos &opt, + expr &out) + { + if (opt.has_index() || ! opt.opt->is_vector()) + ctx->throw_exception("parameter of size() is not a vector variable", opt.it_range); + out.set_i(int(static_cast(opt.opt)->size())); + out.it_range = opt.it_range; + } + // Verify that the expression returns an integer, which may be used // to address a vector. template @@ -1843,8 +1867,7 @@ namespace client assignment_statement = variable_reference(_r1)[_a = _1] >> '=' > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. - (lit('(') > initializer_list(_r1) > ')') - [px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] + initializer_list(_r1)[px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. | eps(px::bind(&MyContext::is_vector_variable_reference, _a)) >> @@ -1859,8 +1882,7 @@ namespace client new_variable_statement = (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable, _r1, _a, _1, _b)] > lit('=') > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. - (lit('(') > initializer_list(_r1) > ')') - [px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)] + initializer_list(_r1)[px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. | eps(px::bind(&MyContext::could_be_vector_variable_reference, _b)) >> @@ -1871,7 +1893,13 @@ namespace client | (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") [px::bind(&MyContext::vector_variable_new_from_array, _r1, _a, _b, _1, _2)] ); - initializer_list = *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]); + initializer_list = lit('(') > + ( lit(')') | + ( conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)] > + *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]) > + lit(')') + ) + ); struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) @@ -1917,6 +1945,8 @@ namespace client | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] | (kw["is_nil"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_nil_test, _r1, _1, _val)] | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] + | (kw["empty"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_vector_empty, _r1, _1, _val)] + | (kw["size"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::vector_size, _r1, _1, _val)] | (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] @@ -1975,6 +2005,7 @@ namespace client ("and") ("digits") ("zdigits") + ("empty") ("if") ("int") ("is_nil") @@ -1994,6 +2025,7 @@ namespace client ("not") ("one_of") ("or") + ("size") ("true"); if (0) { diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 48fe43117..b29ca0f8e 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -222,4 +222,9 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, true)}{mybool[1]}", 0, &config, nullptr, nullptr) == "true"); REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, false)}{mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); } + + SECTION("size() of a non-empty vector returns the right size") { REQUIRE(parser.process("{local myint = (0, 1, 2, 3)}{size(myint)}", 0, nullptr, nullptr, nullptr) == "4"); } + SECTION("size() of a an empty vector returns the right size") { REQUIRE(parser.process("{local myint = (0);myint=();size(myint)}", 0, nullptr, nullptr, nullptr) == "0"); } + SECTION("empty() of a non-empty vector returns false") { REQUIRE(parser.process("{local myint = (0, 1, 2, 3)}{empty(myint)}", 0, nullptr, nullptr, nullptr) == "false"); } + SECTION("empty() of a an empty vector returns true") { REQUIRE(parser.process("{local myint = (0);myint=();empty(myint)}", 0, nullptr, nullptr, nullptr) == "true"); } } From 19f2a1d9e988175e5673ef17e0b55ae7e5ea349b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 20 Mar 2023 10:38:34 +0100 Subject: [PATCH 139/201] Unified usage of localization macros in slic3r/libslic3r --- src/libslic3r/ExtrusionRole.cpp | 1 - src/libslic3r/Flow.cpp | 5 +- src/libslic3r/Format/3mf.cpp | 15 +-- src/libslic3r/Format/AMF.cpp | 6 +- src/libslic3r/Format/SLAArchiveReader.cpp | 1 - src/libslic3r/GCode.cpp | 55 +++++------ src/libslic3r/GCode/PostProcessor.cpp | 9 +- src/libslic3r/I18N.hpp | 7 ++ src/libslic3r/Preset.cpp | 9 -- src/libslic3r/Print.cpp | 96 +++++++++---------- src/libslic3r/PrintBase.cpp | 6 +- src/libslic3r/PrintConfig.cpp | 5 - src/libslic3r/PrintObject.cpp | 16 ++-- src/libslic3r/PrintObjectSlice.cpp | 8 +- src/libslic3r/SLA/Hollowing.cpp | 12 +-- src/libslic3r/SLA/Pad.cpp | 5 +- src/libslic3r/SLA/SupportTree.cpp | 4 - src/libslic3r/SLAPrint.cpp | 14 +-- src/libslic3r/SLAPrintSteps.cpp | 47 +++++---- src/libslic3r/Zipper.cpp | 4 +- src/libslic3r/miniz_extension.cpp | 68 +++++++------ src/slic3r/GUI/BedShapeDialog.cpp | 3 + src/slic3r/GUI/BedShapeDialog.hpp | 4 +- src/slic3r/GUI/ConfigWizard.cpp | 1 + src/slic3r/GUI/GUI_App.cpp | 13 ++- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 22 +++-- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 + src/slic3r/GUI/OG_CustomCtrl.hpp | 1 - 44 files changed, 211 insertions(+), 264 deletions(-) diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp index 1e91df204..01ec73ed1 100644 --- a/src/libslic3r/ExtrusionRole.cpp +++ b/src/libslic3r/ExtrusionRole.cpp @@ -4,7 +4,6 @@ #include #include -#define L(s) (s) namespace Slic3r { diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 9f4730261..1084e6f10 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -6,9 +6,6 @@ #include -// Mark string for localization and translate. -#define L(s) Slic3r::I18N::translate(s) - namespace Slic3r { FlowErrorNegativeSpacing::FlowErrorNegativeSpacing() : @@ -58,7 +55,7 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key) static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key) { - throw FlowErrorMissingVariable((boost::format(L("Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible.")) % opt_key % dependent_opt_key).str()); + throw FlowErrorMissingVariable((boost::format(_u8L("Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible.")) % opt_key % dependent_opt_key).str()); } // Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser. diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 34594240f..5a608cb05 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -296,11 +296,6 @@ bool is_valid_object_type(const std::string& type) namespace Slic3r { -//! macro used to mark string used at localization, -//! return same string -#define L(s) (s) -#define _(s) Slic3r::I18N::translate(s) - // Base class with error messages management class _3MF_Base { @@ -1875,9 +1870,9 @@ namespace Slic3r { if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION) { m_version = (unsigned int)atoi(m_curr_characters.c_str()); if (m_check_version && (m_version > VERSION_3MF_COMPATIBLE)) { - // std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); + // std::string msg = _u8L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."); // throw version_error(msg.c_str()); - const std::string msg = (boost::format(_(L("The selected 3mf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str(); + const std::string msg = (boost::format(_u8L("The selected 3mf file has been saved with a newer version of %1% and is not compatible.")) % std::string(SLIC3R_APP_NAME)).str(); throw version_error(msg); } } else if (m_curr_metadata_name == "Application") { @@ -1888,15 +1883,15 @@ namespace Slic3r { } else if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) { m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str()); check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION, - _(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible."))); + _u8L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")); } else if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) { m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str()); check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION, - _(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible."))); + _u8L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")); } else if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) { m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str()); check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION, - _(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible."))); + _u8L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")); } return true; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index a38960324..c72855c88 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -57,10 +57,6 @@ const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; namespace Slic3r { -//! macro used to mark string used at localization, -//! return same string -#define L(s) (s) -#define _(s) Slic3r::I18N::translate(s) struct AMFParserContext { @@ -997,7 +993,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi { // std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); // throw Slic3r::FileIOError(msg.c_str()); - const std::string msg = (boost::format(_(L("The selected amf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str(); + const std::string msg = (boost::format(_u8L("The selected amf file has been saved with a newer version of %1% and is not compatible.")) % std::string(SLIC3R_APP_NAME)).str(); throw Slic3r::FileIOError(msg); } diff --git a/src/libslic3r/Format/SLAArchiveReader.cpp b/src/libslic3r/Format/SLAArchiveReader.cpp index 09c157059..f80b9a2ba 100644 --- a/src/libslic3r/Format/SLAArchiveReader.cpp +++ b/src/libslic3r/Format/SLAArchiveReader.cpp @@ -7,7 +7,6 @@ #include #include -constexpr const char * L(const char * str) { return str; } #include #include diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 65549d2e8..2fca25c96 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -71,11 +71,6 @@ using namespace std::literals::string_view_literals; namespace Slic3r { - //! macro used to mark string used at localization, - //! return same string -#define L(s) (s) -#define _(s) Slic3r::I18N::translate(s) - // Only add a newline in case the current G-code does not end with a newline. static inline void check_add_eol(std::string& gcode) { @@ -487,8 +482,8 @@ GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& obj // first layer may result in skirt/brim in the air and maybe other issues. if (layers_to_print.size() == 1u) { if (!has_extrusions) - throw Slic3r::SlicingError(_(L("There is an object with no extrusions in the first layer.")) + "\n" + - _(L("Object name")) + ": " + object.model_object()->name); + throw Slic3r::SlicingError(_u8L("There is an object with no extrusions in the first layer.") + "\n" + + _u8L("Object name") + ": " + object.model_object()->name); } // In case there are extrusions on this layer, check there is a layer to lay it on. @@ -518,14 +513,14 @@ GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& obj std::string warning; size_t i = 0; for (i = 0; i < std::min(warning_ranges.size(), size_t(3)); ++i) - warning += Slic3r::format(_(L("Empty layer between %1% and %2%.")), + warning += Slic3r::format(_u8L("Empty layer between %1% and %2%."), warning_ranges[i].first, warning_ranges[i].second) + "\n"; if (i < warning_ranges.size()) - warning += _(L("(Some lines not shown)")) + "\n"; + warning += _u8L("(Some lines not shown)") + "\n"; warning += "\n"; - warning += Slic3r::format(_(L("Object name: %1%")), object.model_object()->name) + "\n\n" - + _(L("Make sure the object is printable. This is usually caused by negligibly small extrusions or by a faulty model. " - "Try to repair the model or change its orientation on the bed.")); + warning += Slic3r::format(_u8L("Object name: %1%"), object.model_object()->name) + "\n\n" + + _u8L("Make sure the object is printable. This is usually caused by negligibly small extrusions or by a faulty model. " + "Try to repair the model or change its orientation on the bed."); const_cast(object.print())->active_step_add_warning( PrintStateBase::WarningLevel::CRITICAL, warning); @@ -655,25 +650,25 @@ namespace DoExport { }; const GCodeConfig& config = print.config(); - check(_(L("Start G-code")), config.start_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("End G-code")), config.end_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("Before layer change G-code")), config.before_layer_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("After layer change G-code")), config.layer_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("Tool change G-code")), config.toolchange_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("Between objects G-code (for sequential printing)")), config.between_objects_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause Print G-code")), config.pause_print_gcode.value); - if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value); + check(_u8L("Start G-code"), config.start_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("End G-code"), config.end_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("Before layer change G-code"), config.before_layer_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("After layer change G-code"), config.layer_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("Tool change G-code"), config.toolchange_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("Between objects G-code (for sequential printing)"), config.between_objects_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("Color Change G-code"), config.color_change_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("Pause Print G-code"), config.pause_print_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_u8L("Template Custom G-code"), config.template_custom_gcode.value); if (ret.size() < MAX_TAGS_COUNT) { for (const std::string& value : config.start_filament_gcode.values) { - check(_(L("Filament Start G-code")), value); + check(_u8L("Filament Start G-code"), value); if (ret.size() == MAX_TAGS_COUNT) break; } } if (ret.size() < MAX_TAGS_COUNT) { for (const std::string& value : config.end_filament_gcode.values) { - check(_(L("Filament End G-code")), value); + check(_u8L("Filament End G-code"), value); if (ret.size() == MAX_TAGS_COUNT) break; } @@ -681,7 +676,7 @@ namespace DoExport { if (ret.size() < MAX_TAGS_COUNT) { const CustomGCode::Info& custom_gcode_per_print_z = print.model().custom_gcode_per_print_z; for (const auto& gcode : custom_gcode_per_print_z.gcodes) { - check(_(L("Custom G-code")), gcode.extra); + check(_u8L("Custom G-code"), gcode.extra); if (ret.size() == MAX_TAGS_COUNT) break; } @@ -714,9 +709,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu reports += source + ": \"" + keyword + "\"\n"; } print->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - _(L("In the custom G-code were found reserved keywords:")) + "\n" + + _u8L("In the custom G-code were found reserved keywords:") + "\n" + reports + - _(L("This may cause problems in g-code visualization and printing time estimation."))); + _u8L("This may cause problems in g-code visualization and printing time estimation.")); } BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); @@ -1111,7 +1106,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } if (initial_extruder_id == static_cast(-1)) // No object to print was found, cancel the G-code export. - throw Slic3r::SlicingError(_(L("No extrusions were generated for objects."))); + throw Slic3r::SlicingError(_u8L("No extrusions were generated for objects.")); // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. // Use the extruder IDs collected from Regions. this->set_extruders(print.extruders()); @@ -1122,7 +1117,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato tool_ordering.assign_custom_gcodes(print); if (tool_ordering.all_extruders().empty()) // No object to print was found, cancel the G-code export. - throw Slic3r::SlicingError(_(L("No extrusions were generated for objects."))); + throw Slic3r::SlicingError(_u8L("No extrusions were generated for objects.")); has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? // The priming towers will be skipped. @@ -1316,8 +1311,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // (See https://github.com/prusa3d/PrusaSlicer/issues/5441.) if (overlap) { print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - _(L("Your print is very close to the priming regions. " - "Make sure there is no collision."))); + _u8L("Your print is very close to the priming regions. " + "Make sure there is no collision.")); } else { // Just continue printing, no action necessary. } diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index de1807dbb..c434aa560 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -185,11 +185,6 @@ static int run_script(const std::string &script, const std::string &gcode, std:: namespace Slic3r { -//! macro used to mark string used at localization, -//! return same string -#define L(s) (s) -#define _(s) Slic3r::I18N::translate(s) - // Run post processing script / scripts if defined. // Returns true if a post-processing script was executed. // Returns false if no post-processing script was defined. @@ -285,10 +280,10 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: throw Slic3r::RuntimeError(msg); } if (! boost::filesystem::exists(gcode_file)) { - const std::string msg = (boost::format(_(L( + const std::string msg = (boost::format(_u8L( "Post-processing script %1% failed.\n\n" "The post-processing script is expected to change the G-code file %2% in place, but the G-code file was deleted and likely saved under a new name.\n" - "Please adjust the post-processing script to change the G-code in place and consult the manual on how to optionally rename the post-processed G-code file.\n"))) + "Please adjust the post-processing script to change the G-code in place and consult the manual on how to optionally rename the post-processed G-code file.\n")) % script % path).str(); BOOST_LOG_TRIVIAL(error) << msg; throw Slic3r::RuntimeError(msg); diff --git a/src/libslic3r/I18N.hpp b/src/libslic3r/I18N.hpp index db4fd22df..3bf286b19 100644 --- a/src/libslic3r/I18N.hpp +++ b/src/libslic3r/I18N.hpp @@ -15,4 +15,11 @@ namespace I18N { } // namespace Slic3r +namespace { + const char* L(const char* s) { return s; } + std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } + std::string _utf8(const char* s) { return Slic3r::I18N::translate(s); } + std::string _utf8(const std::string& s) { return Slic3r::I18N::translate(s); } +} + #endif /* slic3r_I18N_hpp_ */ diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index b36427175..3598d36ae 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -10,15 +10,6 @@ #include #endif /* _MSC_VER */ -// instead of #include "slic3r/GUI/I18N.hpp" : -#ifndef L -// !!! If you needed to translate some string, -// !!! please use _L(string) -// !!! _() - is a standard wxWidgets macro to translate -// !!! L() is used only for marking localizable string -// !!! It will be used in "xgettext" to create a Locating Message Catalog. -#define L(s) s -#endif /* L */ #include #include diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f2143857a..0dd20ea93 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -27,8 +27,6 @@ #include #include -// Mark string for localization and translate. -#define L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -474,20 +472,20 @@ std::string Print::validate(std::string* warning) const std::vector extruders = this->extruders(); if (m_objects.empty()) - return L("All objects are outside of the print volume."); + return _u8L("All objects are outside of the print volume."); if (extruders.empty()) - return L("The supplied settings will cause an empty print."); + return _u8L("The supplied settings will cause an empty print."); if (m_config.complete_objects) { if (! sequential_print_horizontal_clearance_valid(*this)) - return L("Some objects are too close; your extruder will collide with them."); + return _u8L("Some objects are too close; your extruder will collide with them."); if (! sequential_print_vertical_clearance_valid(*this)) - return L("Some objects are too tall and cannot be printed without extruder collisions."); + return _u8L("Some objects are too tall and cannot be printed without extruder collisions."); } if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) { - return L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together."); + return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together."); } if (m_config.spiral_vase) { @@ -496,11 +494,11 @@ std::string Print::validate(std::string* warning) const total_copies_count += object->instances().size(); // #4043 if (total_copies_count > 1 && ! m_config.complete_objects.value) - return L("Only a single object may be printed at a time in Spiral Vase mode. " + return _u8L("Only a single object may be printed at a time in Spiral Vase mode. " "Either remove all but the last object, or enable sequential mode by \"complete_objects\"."); assert(m_objects.size() == 1); if (m_objects.front()->all_regions().size() > 1) - return L("The Spiral Vase option can only be used when printing single material objects."); + return _u8L("The Spiral Vase option can only be used when printing single material objects."); } // Cache of layer height profiles for checking: @@ -524,7 +522,7 @@ std::string Print::validate(std::string* warning) const //FIXME It is quite expensive to generate object layers just to get the print height! if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx)); ! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) { - return L("The print is taller than the maximum allowed height. You might want to reduce the size of your model" + return _u8L("The print is taller than the maximum allowed height. You might want to reduce the size of your model" " or change current print settings and retry."); } } @@ -541,7 +539,7 @@ std::string Print::validate(std::string* warning) const print_object.model_object()->has_custom_layering()) { if (const std::vector &layers = layer_height_profile(print_object_idx); ! layers.empty()) if (! check_object_layers_fixed(print_object.slicing_parameters(), layers)) - return L("Variable layer height is not supported with Organic supports."); + return _u8L("Variable layer height is not supported with Organic supports."); } if (this->has_wipe_tower() && ! m_objects.empty()) { @@ -554,21 +552,21 @@ std::string Print::validate(std::string* warning) const double filament_diam = m_config.filament_diameter.get_at(extruder_idx); if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam || std::abs((filament_diam-first_filament_diam)/first_filament_diam) > 0.1) - return L("The wipe tower is only supported if all extruders have the same nozzle diameter " + return _u8L("The wipe tower is only supported if all extruders have the same nozzle diameter " "and use filaments of the same diameter."); } if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlinLegacy && m_config.gcode_flavor != gcfMarlinFirmware) - return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors."); + return _u8L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors."); if (! m_config.use_relative_e_distances) - return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); + return _u8L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); if (m_config.ooze_prevention && m_config.single_extruder_multi_material) - return L("Ooze prevention is only supported with the wipe tower when 'single_extruder_multi_material' is off."); + return _u8L("Ooze prevention is only supported with the wipe tower when 'single_extruder_multi_material' is off."); if (m_config.use_volumetric_e) - return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); + return _u8L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); if (m_config.complete_objects && extruders.size() > 1) - return L("The Wipe Tower is currently not supported for multimaterial sequential prints."); + return _u8L("The Wipe Tower is currently not supported for multimaterial sequential prints."); if (m_objects.size() > 1) { const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters(); @@ -578,14 +576,14 @@ std::string Print::validate(std::string* warning) const const SlicingParameters &slicing_params = object->slicing_parameters(); if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON || std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON) - return L("The Wipe Tower is only supported for multiple objects if they have equal layer heights"); + return _u8L("The Wipe Tower is only supported for multiple objects if they have equal layer heights"); if (slicing_params.raft_layers() != slicing_params0.raft_layers()) - return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"); + return _u8L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"); if (slicing_params0.gap_object_support != slicing_params.gap_object_support || slicing_params0.gap_support_object != slicing_params.gap_support_object) - return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"); + return _u8L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"); if (! equal_layering(slicing_params, slicing_params0)) - return L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); + return _u8L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); if (has_custom_layering) { auto &lh = layer_height_profile(i); auto &lh_tallest = layer_height_profile(tallest_object_idx); @@ -610,7 +608,7 @@ std::string Print::validate(std::string* warning) const if (i%2 == 0 && layer_height_profiles[tallest_object_idx][i] > layer_height_profiles[idx_object][layer_height_profiles[idx_object].size() - 2 ]) break; if (std::abs(layer_height_profiles[idx_object][i] - layer_height_profiles[tallest_object_idx][i]) > eps) - return L("The Wipe tower is only supported if all objects have the same variable layer height"); + return _u8L("The Wipe tower is only supported if all objects have the same variable layer height"); ++i; } } @@ -634,7 +632,7 @@ std::string Print::validate(std::string* warning) const unsigned int total_extruders_count = m_config.nozzle_diameter.size(); for (const auto& extruder_idx : extruders) if ( extruder_idx >= total_extruders_count ) - return L("One or more object were assigned an extruder that the printer does not have."); + return _u8L("One or more object were assigned an extruder that the printer does not have."); #endif auto validate_extrusion_width = [/*min_nozzle_diameter,*/ max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool { @@ -647,10 +645,10 @@ std::string Print::validate(std::string* warning) const if (extrusion_width_min == 0) { // Default "auto-generated" extrusion width is always valid. } else if (extrusion_width_min <= layer_height) { - err_msg = (boost::format(L("%1%=%2% mm is too low to be printable at a layer height %3% mm")) % opt_key % extrusion_width_min % layer_height).str(); + err_msg = (boost::format(_u8L("%1%=%2% mm is too low to be printable at a layer height %3% mm")) % opt_key % extrusion_width_min % layer_height).str(); return false; } else if (extrusion_width_max >= max_nozzle_diameter * 3.) { - err_msg = (boost::format(L("Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm")) % opt_key % extrusion_width_max % max_nozzle_diameter).str(); + err_msg = (boost::format(_u8L("Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm")) % opt_key % extrusion_width_max % max_nozzle_diameter).str(); return false; } return true; @@ -661,7 +659,7 @@ std::string Print::validate(std::string* warning) const // The object has some form of support and either support_material_extruder or support_material_interface_extruder // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles // are of the same diameter. - return L("Printing with multiple extruders of differing nozzle diameters. " + return _u8L("Printing with multiple extruders of differing nozzle diameters. " "If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), " "all nozzles have to be of the same diameter."); } @@ -669,11 +667,11 @@ std::string Print::validate(std::string* warning) const if (object->config().support_material_contact_distance == 0) { // Soluble interface if (! object->config().support_material_synchronize_layers) - return L("For the Wipe Tower to work with the soluble supports, the support layers need to be synchronized with the object layers."); + return _u8L("For the Wipe Tower to work with the soluble supports, the support layers need to be synchronized with the object layers."); } else { // Non-soluble interface if (object->config().support_material_extruder != 0 || object->config().support_material_interface_extruder != 0) - return L("The Wipe Tower currently supports the non-soluble supports only if they are printed with the current extruder without triggering a tool change. " + return _u8L("The Wipe Tower currently supports the non-soluble supports only if they are printed with the current extruder without triggering a tool change. " "(both support_material_extruder and support_material_interface_extruder need to be set to 0)."); } } @@ -709,12 +707,12 @@ std::string Print::validate(std::string* warning) const first_layer_min_nozzle_diameter = min_nozzle_diameter; } if (first_layer_height > first_layer_min_nozzle_diameter) - return L("First layer height can't be greater than nozzle diameter"); + return _u8L("First layer height can't be greater than nozzle diameter"); // validate layer_height double layer_height = object->config().layer_height.value; if (layer_height > min_nozzle_diameter) - return L("Layer height can't be greater than nozzle diameter"); + return _u8L("Layer height can't be greater than nozzle diameter"); // Validate extrusion widths. std::string err_msg; @@ -735,11 +733,11 @@ std::string Print::validate(std::string* warning) const // See GH issues #6336 #5073 if ((m_config.gcode_flavor == gcfMarlinLegacy || m_config.gcode_flavor == gcfMarlinFirmware) && ! before_layer_gcode_resets_extruder && ! layer_gcode_resets_extruder) - return L("Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."); + return _u8L("Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."); } else if (before_layer_gcode_resets_extruder) - return L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder addressing."); + return _u8L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder addressing."); else if (layer_gcode_resets_extruder) - return L("\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing."); + return _u8L("\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing."); } return std::string(); @@ -881,7 +879,7 @@ void Print::process() BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); - this->set_status(70, L("Infilling layers")); + this->set_status(70, _u8L("Infilling layers")); for (PrintObject *obj : m_objects) obj->infill(); for (PrintObject *obj : m_objects) @@ -898,7 +896,7 @@ void Print::process() m_wipe_tower_data.clear(); m_tool_ordering.clear(); if (this->has_wipe_tower()) { - //this->set_status(95, L("Generating wipe tower")); + //this->set_status(95, _u8L("Generating wipe tower")); this->_make_wipe_tower(); } else if (! this->config().complete_objects.value) { // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches. @@ -909,7 +907,7 @@ void Print::process() this->set_done(psWipeTower); } if (this->set_started(psSkirtBrim)) { - this->set_status(88, L("Generating skirt and brim")); + this->set_status(88, _u8L("Generating skirt and brim")); m_skirt.clear(); m_skirt_convex_hull.clear(); @@ -957,11 +955,11 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor std::string message; if (!path.empty() && result == nullptr) { // Only show the path if preview_data is not set -> running from command line. - message = L("Exporting G-code"); + message = _u8L("Exporting G-code"); message += " to "; message += path; } else - message = L("Generating G-code"); + message = _u8L("Generating G-code"); this->set_status(90, message); // Create GCode on heap, it has quite a lot of data. @@ -1181,30 +1179,30 @@ void Print::alert_when_supports_needed() { if (this->set_started(psAlertWhenSupportsNeeded)) { BOOST_LOG_TRIVIAL(debug) << "psAlertWhenSupportsNeeded - start"; - set_status(69, L("Alert if supports needed")); + set_status(69, _u8L("Alert if supports needed")); auto issue_to_alert_message = [](SupportSpotsGenerator::SupportPointCause cause, bool critical) { std::string message; switch (cause) { //TRN Alert when support is needed. Describes that the model has long bridging extrusions which may print badly - case SupportSpotsGenerator::SupportPointCause::LongBridge: message = L("Long bridging extrusions"); break; + case SupportSpotsGenerator::SupportPointCause::LongBridge: message = _u8L("Long bridging extrusions"); break; //TRN Alert when support is needed. Describes bridge anchors/turns in the air, which will definitely print badly - case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: message = L("Floating bridge anchors"); break; + case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: message = _u8L("Floating bridge anchors"); break; case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion: if (critical) { //TRN Alert when support is needed. Describes that the print has large overhang area which will print badly or not print at all. - message = L("Collapsing overhang"); + message = _u8L("Collapsing overhang"); } else { //TRN Alert when support is needed. Describes extrusions that are not supported enough and come out curled or loose. - message = L("Loose extrusions"); + message = _u8L("Loose extrusions"); } break; //TRN Alert when support is needed. Describes that the print has low bed adhesion and may became loose. - case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: message = L("Low bed adhesion"); break; + case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: message = _u8L("Low bed adhesion"); break; //TRN Alert when support is needed. Describes that the object has part that is not connected to the bed and will not print at all without supports. - case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: message = L("Floating object part"); break; + case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: message = _u8L("Floating object part"); break; //TRN Alert when support is needed. Describes that the object has thin part that may brake during printing - case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("Thin fragile part"); break; + case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = _u8L("Thin fragile part"); break; } return message; @@ -1312,13 +1310,13 @@ void Print::alert_when_supports_needed() } lines.push_back(""); - lines.push_back(L("Consider enabling supports.")); + lines.push_back(_u8L("Consider enabling supports.")); if (recommend_brim) { - lines.push_back(L("Also consider enabling brim.")); + lines.push_back(_u8L("Also consider enabling brim.")); } // TRN Alert message for detected print issues. first argument is a list of detected issues. - auto message = Slic3r::format(L("Detected print stability issues:\n%1%"), elements_to_translated_list(lines, multiline_list_rule)); + auto message = Slic3r::format(_u8L("Detected print stability issues:\n%1%"), elements_to_translated_list(lines, multiline_list_rule)); if (objects_isssues.size() > 0) { this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, message); diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index d9b3e9cda..01c0ecfaa 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -6,10 +6,6 @@ #include "I18N.hpp" -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) - namespace Slic3r { @@ -81,7 +77,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str filename = boost::filesystem::change_extension(filename, default_ext); return filename.string(); } catch (std::runtime_error &err) { - throw Slic3r::PlaceholderParserError(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); + throw Slic3r::PlaceholderParserError(_u8L("Failed processing of the output_filename_format template.") + "\n" + err.what()); } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3255c015b..d52f38866 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -16,11 +16,6 @@ namespace Slic3r { -//! macro used to mark string used at localization, -//! return same string -#define L(s) (s) -#define _(s) Slic3r::I18N::translate(s) - static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values &enum_keys_map) { t_config_enum_names names; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index aa1216a0f..eefedd143 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -56,10 +56,6 @@ using namespace std::literals; -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) - #ifdef SLIC3R_DEBUG_SLICE_PROCESSING #define SLIC3R_DEBUG #endif @@ -153,7 +149,7 @@ void PrintObject::make_perimeters() if (! this->set_started(posPerimeters)) return; - m_print->set_status(20, L("Generating perimeters")); + m_print->set_status(20, _u8L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // Revert the typed slices into untyped slices. @@ -258,7 +254,7 @@ void PrintObject::prepare_infill() if (! this->set_started(posPrepareInfill)) return; - m_print->set_status(30, L("Preparing infill")); + m_print->set_status(30, _u8L("Preparing infill")); if (m_typed_slices) { // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. @@ -403,7 +399,7 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { - m_print->set_status(45, L("making infill")); + m_print->set_status(45, _u8L("making infill")); const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; @@ -450,7 +446,7 @@ void PrintObject::generate_support_spots() { if (this->set_started(posSupportSpotsSearch)) { BOOST_LOG_TRIVIAL(debug) << "Searching support spots - start"; - m_print->set_status(65, L("Searching support spots")); + m_print->set_status(65, _u8L("Searching support spots")); if (!this->shared_regions()->generated_support_points.has_value()) { PrintTryCancel cancel_func = m_print->make_try_cancel(); SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, @@ -475,7 +471,7 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(70, L("Generating support material")); + m_print->set_status(70, _u8L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -496,7 +492,7 @@ void PrintObject::estimate_curled_extrusions() if (this->set_started(posEstimateCurledExtrusions)) { if (this->print()->config().avoid_crossing_curled_overhangs) { BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start"; - m_print->set_status(88, L("Estimating curled extrusions")); + m_print->set_status(88, _u8L("Estimating curled extrusions")); // Estimate curling of support material and add it to the malformaition lines of each layer float support_flow_width = support_material_flow(this, this->config().layer_height).width(); diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 4cc5adff0..6f6080cb8 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -10,8 +10,6 @@ #include -//! macro used to mark string used at localization, return same string -#define L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -499,7 +497,7 @@ void PrintObject::slice() { if (! this->set_started(posSlice)) return; - m_print->set_status(10, L("Processing triangulated mesh")); + m_print->set_status(10, _u8L("Processing triangulated mesh")); std::vector layer_height_profile; this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile); m_print->throw_if_canceled(); @@ -733,9 +731,9 @@ void PrintObject::slice_volumes() if (m_config.xy_size_compensation.value != 0.f) { this->active_step_add_warning( PrintStateBase::WarningLevel::CRITICAL, - L("An object has enabled XY Size compensation which will not be used because it is also multi-material painted.\nXY Size " + _u8L("An object has enabled XY Size compensation which will not be used because it is also multi-material painted.\nXY Size " "compensation cannot be combined with multi-material painting.") + - "\n" + (L("Object name")) + ": " + this->model_object()->name); + "\n" + (_u8L("Object name")) + ": " + this->model_object()->name); } BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - MMU segmentation"; diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 634cde469..56cecdccd 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -23,10 +23,6 @@ #include #include -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) - namespace Slic3r { namespace sla { @@ -83,12 +79,12 @@ InteriorPtr generate_interior(const VoxelGrid &vgrid, auto narrowb = 1.f; // voxel units (voxel count) if (ctl.stopcondition()) return {}; - else ctl.statuscb(0, L("Hollowing")); + else ctl.statuscb(0, _u8L("Hollowing")); auto gridptr = dilate_grid(vgrid, out_range, in_range); if (ctl.stopcondition()) return {}; - else ctl.statuscb(30, L("Hollowing")); + else ctl.statuscb(30, _u8L("Hollowing")); double iso_surface = D; if (D > EPSILON) { @@ -103,7 +99,7 @@ InteriorPtr generate_interior(const VoxelGrid &vgrid, } if (ctl.stopcondition()) return {}; - else ctl.statuscb(70, L("Hollowing")); + else ctl.statuscb(70, _u8L("Hollowing")); double adaptivity = 0.; InteriorPtr interior = InteriorPtr{new Interior{}}; @@ -112,7 +108,7 @@ InteriorPtr generate_interior(const VoxelGrid &vgrid, interior->gridptr = std::move(gridptr); if (ctl.stopcondition()) return {}; - else ctl.statuscb(100, L("Hollowing")); + else ctl.statuscb(100, _u8L("Hollowing")); interior->iso_surface = iso_surface; interior->thickness = offset; diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index ec9e216f5..34a1b5dd3 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -21,9 +21,6 @@ #include "I18N.hpp" #include -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) namespace Slic3r { namespace sla { @@ -530,7 +527,7 @@ std::string PadConfig::validate() const if (brim_size_mm < MIN_BRIM_SIZE_MM || bottom_offset() > brim_size_mm + wing_distance() || get_waffle_offset(*this) <= MIN_BRIM_SIZE_MM) - return L("Pad brim size is too small for the current configuration."); + return _u8L("Pad brim size is too small for the current configuration."); return ""; } diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index 3de05261a..d066e02bf 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -16,13 +16,9 @@ #include #include -#include #include -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) namespace Slic3r { namespace sla { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 202da5e00..6dc4a4620 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -23,7 +23,7 @@ //! macro used to mark string used at localization, //! return same string -#define L(s) Slic3r::I18N::translate(s) +#define _u8L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -543,7 +543,7 @@ std::string SLAPrint::validate(std::string*) const if(supports_en && mo->sla_points_status == sla::PointsStatus::UserModified && mo->sla_support_points.empty()) - return L("Cannot proceed without support points! " + return _u8L("Cannot proceed without support points! " "Add support points or disable support generation."); sla::SupportTreeConfig cfg = make_support_cfg(po->config()); @@ -554,13 +554,13 @@ std::string SLAPrint::validate(std::string*) const sla::PadConfig::EmbedObject &builtinpad = padcfg.embed_object; if(supports_en && !builtinpad.enabled && elv < cfg.head_fullwidth()) - return L( + return _u8L( "Elevation is too low for object. Use the \"Pad around " "object\" feature to print the object without elevation."); if(supports_en && builtinpad.enabled && cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) { - return L( + return _u8L( "The endings of the support pillars will be deployed on the " "gap between the object and the pad. 'Support base safety " "distance' has to be greater than the 'Pad object gap' " @@ -576,14 +576,14 @@ std::string SLAPrint::validate(std::string*) const double expt_cur = m_material_config.exposure_time.getFloat(); if (expt_cur < expt_min || expt_cur > expt_max) - return L("Exposition time is out of printer profile bounds."); + return _u8L("Exposition time is out of printer profile bounds."); double iexpt_max = m_printer_config.max_initial_exposure_time.getFloat(); double iexpt_min = m_printer_config.min_initial_exposure_time.getFloat(); double iexpt_cur = m_material_config.initial_exposure_time.getFloat(); if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max) - return L("Initial exposition time is out of printer profile bounds."); + return _u8L("Initial exposition time is out of printer profile bounds."); return ""; } @@ -690,7 +690,7 @@ void SLAPrint::process() } // If everything vent well - m_report_status(*this, 100, L("Slicing done")); + m_report_status(*this, 100, _u8L("Slicing done")); #ifdef SLAPRINT_DO_BENCHMARK std::string csvbenchstr; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 338130454..94ddcec72 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -32,9 +32,6 @@ #include -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -54,14 +51,14 @@ const std::array OBJ_STEP_LEVELS = { std::string OBJ_STEP_LABELS(size_t idx) { switch (idx) { - case slaposAssembly: return L("Assembling model from parts"); - case slaposHollowing: return L("Hollowing model"); - case slaposDrillHoles: return L("Drilling holes into model."); - case slaposObjectSlice: return L("Slicing model"); - case slaposSupportPoints: return L("Generating support points"); - case slaposSupportTree: return L("Generating support tree"); - case slaposPad: return L("Generating pad"); - case slaposSliceSupports: return L("Slicing supports"); + case slaposAssembly: return _u8L("Assembling model from parts"); + case slaposHollowing: return _u8L("Hollowing model"); + case slaposDrillHoles: return _u8L("Drilling holes into model."); + case slaposObjectSlice: return _u8L("Slicing model"); + case slaposSupportPoints: return _u8L("Generating support points"); + case slaposSupportTree: return _u8L("Generating support tree"); + case slaposPad: return _u8L("Generating pad"); + case slaposSliceSupports: return _u8L("Slicing supports"); default:; } assert(false); @@ -76,8 +73,8 @@ const std::array PRINT_STEP_LEVELS = { std::string PRINT_STEP_LABELS(size_t idx) { switch (idx) { - case slapsMergeSlicesAndEval: return L("Merging slices and calculating statistics"); - case slapsRasterize: return L("Rasterizing layers"); + case slapsMergeSlicesAndEval: return _u8L("Merging slices and calculating statistics"); + case slapsRasterize: return _u8L("Rasterizing layers"); default:; } assert(false); return "Out of bounds!"; @@ -252,14 +249,14 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st if (ret & static_cast(sla::HollowMeshResult::FaultyMesh)) { po.active_step_add_warning( PrintStateBase::WarningLevel::NON_CRITICAL, - L("Mesh to be hollowed is not suitable for hollowing (does not " + _u8L("Mesh to be hollowed is not suitable for hollowing (does not " "bound a volume).")); } if (ret & static_cast(sla::HollowMeshResult::FaultyHoles)) { po.active_step_add_warning( PrintStateBase::WarningLevel::NON_CRITICAL, - L("Unable to drill the current configuration of holes into the " + _u8L("Unable to drill the current configuration of holes into the " "model.")); } @@ -267,7 +264,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st if (ret & static_cast(sla::HollowMeshResult::DrillingFailed)) { po.active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, L( + PrintStateBase::WarningLevel::NON_CRITICAL, _u8L( "Drilling holes into the mesh failed. " "This is usually caused by broken model. Try to fix it first.")); @@ -276,7 +273,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st if (hole_fail) { po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("Failed to drill some holes into the model")); + _u8L("Failed to drill some holes into the model")); handled = false; } @@ -286,7 +283,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st if (!handled) { // Last resort to voxelization. po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("Can't perform full mesh booleans! " + _u8L("Can't perform full mesh booleans! " "Some parts of the print will be previewed with approximated meshes. " "This does not affect the quality of slices or the physical print in any way.")); m = generate_preview_vdb(po, step); @@ -507,7 +504,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) if(slindex_it == po.m_slice_index.end()) //TRN To be shown at the status bar on SLA slicing error. throw Slic3r::RuntimeError( - L("Slicing had to be stopped due to an internal error: " + _u8L("Slicing had to be stopped due to an internal error: " "Inconsistent slice index.")); po.m_model_height_levels.clear(); @@ -688,7 +685,7 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass // the update status to GLGizmoSlaSupports - report_status(-1, L("Generating support points"), + report_status(-1, _u8L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); } else { // There are either some points on the front-end, or the user @@ -737,7 +734,7 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) auto rc = SlicingStatus::RELOAD_SCENE; // This is to prevent "Done." being displayed during merged_mesh() - report_status(-1, L("Visualizing supports")); + report_status(-1, _u8L("Visualizing supports")); BOOST_LOG_TRIVIAL(debug) << "Processed support point count " << po.m_supportdata->input.pts.size(); @@ -746,7 +743,7 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) if(po.support_mesh().empty()) BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; - report_status(-1, L("Visualizing supports"), rc); + report_status(-1, _u8L("Visualizing supports"), rc); } void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { @@ -776,7 +773,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { if (!validate_pad(po.m_supportdata->pad_mesh.its, pcfg)) throw Slic3r::SlicingError( - L("No pad can be generated for this model with the " + _u8L("No pad can be generated for this model with the " "current configuration")); } else if(po.m_supportdata) { @@ -784,7 +781,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { } throw_if_canceled(); - report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE); + report_status(-1, _u8L("Visualizing supports"), SlicingStatus::RELOAD_SCENE); } // Slicing the support geometries similarly to the model slicing procedure. @@ -905,7 +902,7 @@ void SLAPrint::Steps::initialize_printer_input() for(const SliceRecord& slicerecord : o->get_slice_index()) { if (!slicerecord.is_valid()) throw Slic3r::SlicingError( - L("There are unprintable objects. Try to " + _u8L("There are unprintable objects. Try to " "adjust support settings to make the " "objects printable.")); diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index cebafa633..29ca264ca 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -8,7 +8,7 @@ //! macro used to mark string used at localization, //! return same string -#define L(s) Slic3r::I18N::translate(s) +#define _u8L(s) Slic3r::I18N::translate(s) #if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L #define SLIC3R_NORETURN @@ -24,7 +24,7 @@ public: std::string formatted_errorstr() const { - return L("Error with zip archive") + " " + m_zipname + ": " + + return _u8L("Error with zip archive") + " " + m_zipname + ": " + get_errorstr(); } diff --git a/src/libslic3r/miniz_extension.cpp b/src/libslic3r/miniz_extension.cpp index 76b4cb4e5..b0eccbeb3 100644 --- a/src/libslic3r/miniz_extension.cpp +++ b/src/libslic3r/miniz_extension.cpp @@ -6,11 +6,7 @@ #include "boost/nowide/cstdio.hpp" #endif -#include "I18N.hpp" - -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) +#include "libslic3r/I18N.hpp" namespace Slic3r { @@ -88,67 +84,67 @@ std::string MZ_Archive::get_errorstr(mz_zip_error mz_err) case MZ_ZIP_NO_ERROR: return "no error"; case MZ_ZIP_UNDEFINED_ERROR: - return L("undefined error"); + return _u8L("undefined error"); case MZ_ZIP_TOO_MANY_FILES: - return L("too many files"); + return _u8L("too many files"); case MZ_ZIP_FILE_TOO_LARGE: - return L("file too large"); + return _u8L("file too large"); case MZ_ZIP_UNSUPPORTED_METHOD: - return L("unsupported method"); + return _u8L("unsupported method"); case MZ_ZIP_UNSUPPORTED_ENCRYPTION: - return L("unsupported encryption"); + return _u8L("unsupported encryption"); case MZ_ZIP_UNSUPPORTED_FEATURE: - return L("unsupported feature"); + return _u8L("unsupported feature"); case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: - return L("failed finding central directory"); + return _u8L("failed finding central directory"); case MZ_ZIP_NOT_AN_ARCHIVE: - return L("not a ZIP archive"); + return _u8L("not a ZIP archive"); case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: - return L("invalid header or archive is corrupted"); + return _u8L("invalid header or archive is corrupted"); case MZ_ZIP_UNSUPPORTED_MULTIDISK: - return L("unsupported multidisk archive"); + return _u8L("unsupported multidisk archive"); case MZ_ZIP_DECOMPRESSION_FAILED: - return L("decompression failed or archive is corrupted"); + return _u8L("decompression failed or archive is corrupted"); case MZ_ZIP_COMPRESSION_FAILED: - return L("compression failed"); + return _u8L("compression failed"); case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: - return L("unexpected decompressed size"); + return _u8L("unexpected decompressed size"); case MZ_ZIP_CRC_CHECK_FAILED: - return L("CRC-32 check failed"); + return _u8L("CRC-32 check failed"); case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: - return L("unsupported central directory size"); + return _u8L("unsupported central directory size"); case MZ_ZIP_ALLOC_FAILED: - return L("allocation failed"); + return _u8L("allocation failed"); case MZ_ZIP_FILE_OPEN_FAILED: - return L("file open failed"); + return _u8L("file open failed"); case MZ_ZIP_FILE_CREATE_FAILED: - return L("file create failed"); + return _u8L("file create failed"); case MZ_ZIP_FILE_WRITE_FAILED: - return L("file write failed"); + return _u8L("file write failed"); case MZ_ZIP_FILE_READ_FAILED: - return L("file read failed"); + return _u8L("file read failed"); case MZ_ZIP_FILE_CLOSE_FAILED: - return L("file close failed"); + return _u8L("file close failed"); case MZ_ZIP_FILE_SEEK_FAILED: - return L("file seek failed"); + return _u8L("file seek failed"); case MZ_ZIP_FILE_STAT_FAILED: - return L("file stat failed"); + return _u8L("file stat failed"); case MZ_ZIP_INVALID_PARAMETER: - return L("invalid parameter"); + return _u8L("invalid parameter"); case MZ_ZIP_INVALID_FILENAME: - return L("invalid filename"); + return _u8L("invalid filename"); case MZ_ZIP_BUF_TOO_SMALL: - return L("buffer too small"); + return _u8L("buffer too small"); case MZ_ZIP_INTERNAL_ERROR: - return L("internal error"); + return _u8L("internal error"); case MZ_ZIP_FILE_NOT_FOUND: - return L("file not found"); + return _u8L("file not found"); case MZ_ZIP_ARCHIVE_TOO_LARGE: - return L("archive is too large"); + return _u8L("archive is too large"); case MZ_ZIP_VALIDATION_FAILED: - return L("validation failed"); + return _u8L("validation failed"); case MZ_ZIP_WRITE_CALLBACK_FAILED: - return L("write calledback failed"); + return _u8L("write calledback failed"); default: break; } diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index aadf605a1..759b17644 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -128,6 +128,9 @@ void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup) } } +BedShapeDialog::BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")), + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {} + void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { SetFont(wxGetApp().normal_font()); diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index 2f308a507..2c828a58f 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -5,7 +5,6 @@ #include "GUI_Utils.hpp" #include "2DBed.hpp" -#include "I18N.hpp" #include @@ -93,8 +92,7 @@ class BedShapeDialog : public DPIDialog { BedShapePanel* m_panel; public: - BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")), - wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {} + BedShapeDialog(wxWindow* parent); void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index b8956b948..65d08f195 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -56,6 +56,7 @@ #include "MsgDialog.hpp" #include "UnsavedChangesDialog.hpp" #include "slic3r/Utils/AppUpdater.hpp" +#include "slic3r/GUI/I18N.hpp" #if defined(__linux__) && defined(__WXGTK3__) #define wxLinux_gtk3 true diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 076bef54b..5d3b65632 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -5,7 +5,11 @@ #include "GUI_ObjectManipulation.hpp" #include "GUI_Factories.hpp" #include "format.hpp" -#include "I18N.hpp" + +// Localization headers: include libslic3r version first so everything in this file +// uses the slic3r/GUI version (the macros will take precedence over the functions). +#include "libslic3r/I18N.hpp" +#include "slic3r/GUI/I18N.hpp" #include #include @@ -44,7 +48,6 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/I18N.hpp" #include "libslic3r/PresetBundle.hpp" #include "libslic3r/Color.hpp" @@ -2381,8 +2384,8 @@ void GUI_App::add_config_menu(wxMenuBar *menu) auto local_menu = new wxMenu(); wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt)); - const auto config_wizard_name = _(ConfigWizard::name(true)); - const auto config_wizard_tooltip = from_u8((boost::format(_utf8(L("Run %s"))) % config_wizard_name).str()); + const wxString config_wizard_name = _(ConfigWizard::name(true)); + const wxString config_wizard_tooltip = from_u8((boost::format(_u8L("Run %s")) % config_wizard_name).str()); // Cmd+, is standard on OS X - what about other operating systems? if (is_editor()) { local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); @@ -3461,7 +3464,7 @@ void GUI_App::start_download(std::string url) //lets always init so if the download dest folder was changed, new dest is used boost::filesystem::path dest_folder(app_config->get("url_downloader_dest")); if (dest_folder.empty() || !boost::filesystem::is_directory(dest_folder)) { - std::string msg = _utf8("Could not start URL download. Destination folder is not set. Please choose destination folder in Configuration Wizard."); + std::string msg = _u8L("Could not start URL download. Destination folder is not set. Please choose destination folder in Configuration Wizard."); BOOST_LOG_TRIVIAL(error) << msg; show_error(nullptr, msg); return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index a884a0589..770c3ac41 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -149,15 +149,21 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color) GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : m_parent(parent) - , m_group_id(-1) - , m_state(Off) - , m_shortcut_key(NO_SHORTCUT_KEY_VALUE) + , m_group_id(-1) + , m_state(Off) + , m_shortcut_key(NO_SHORTCUT_KEY_VALUE) , m_icon_filename(icon_filename) , m_sprite_id(sprite_id) , m_imgui(wxGetApp().imgui()) { } + +std::string GLGizmoBase::get_action_snapshot_name() const +{ + return _u8L("Gizmo action"); +} + void GLGizmoBase::set_hover_id(int id) { // do not change hover id during dragging @@ -351,12 +357,12 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) std::string GLGizmoBase::get_name(bool include_shortcut) const { - std::string out = on_get_name(); - if (!include_shortcut) return out; - + std::string out = on_get_name(); + if (!include_shortcut) return out; + int key = get_shortcut_key(); - assert(key==NO_SHORTCUT_KEY_VALUE || (key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z)); - out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; + assert(key==NO_SHORTCUT_KEY_VALUE || (key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z)); + out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; return out; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 108b869da..9ed34b011 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -4,7 +4,6 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Color.hpp" -#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/SceneRaycaster.hpp" @@ -150,7 +149,7 @@ public: virtual bool wants_enter_leave_snapshots() const { return false; } virtual std::string get_gizmo_entering_text() const { assert(false); return ""; } virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; } - virtual std::string get_action_snapshot_name() { return _u8L("Gizmo action"); } + virtual std::string get_action_snapshot_name() const; void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } unsigned int get_sprite_id() const { return m_sprite_id; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e9b6fa694..e4a7e9613 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoCut.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 794d0d339..02ed8d5b6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -4,6 +4,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/I18N.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" #include "imgui/imgui.h" @@ -253,7 +254,7 @@ protected: bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Entering Cut gizmo"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Cut gizmo"); } - std::string get_action_snapshot_name() override { return _u8L("Cut gizmo editing"); } + std::string get_action_snapshot_name() const override { return _u8L("Cut gizmo editing"); } void data_changed() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index ebbdf616c..a57def86e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -1,12 +1,11 @@ #ifndef slic3r_GLGizmoEmboss_hpp_ #define slic3r_GLGizmoEmboss_hpp_ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, -// which overrides our localization "L" macro. #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" #include "slic3r/GUI/IconManager.hpp" #include "slic3r/GUI/SurfaceDrag.hpp" +#include "slic3r/GUI/I18N.hpp" #include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/Utils/EmbossStyleManager.hpp" @@ -76,7 +75,7 @@ protected: bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Enter emboss gizmo"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); } - std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); } + std::string get_action_snapshot_name() const override { return _u8L("Embossing actions"); } private: static EmbossStyles create_default_styles(); // localized default text diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index b79e1dda7..aee669199 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -3,6 +3,8 @@ #include "GLGizmoPainterBase.hpp" +#include "slic3r/GUI/I18N.hpp" + namespace Slic3r::GUI { class GLGizmoFdmSupports : public GLGizmoPainterBase @@ -21,7 +23,7 @@ protected: std::string get_gizmo_entering_text() const override { return _u8L("Entering Paint-on supports"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Paint-on supports"); } - std::string get_action_snapshot_name() override { return _u8L("Paint-on supports editing"); } + std::string get_action_snapshot_name() const override { return _u8L("Paint-on supports editing"); } private: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 947ef2df8..6cbec0891 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoFlatten.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 80520651d..a3184371e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoMeasure.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 4652a171b..cc43b068e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GUI_Utils.hpp" #include "slic3r/GUI/MeshUtils.hpp" +#include "slic3r/GUI/I18N.hpp" #include "libslic3r/Measure.hpp" #include "libslic3r/Model.hpp" @@ -162,7 +163,7 @@ public: bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); } - std::string get_action_snapshot_name() override { return _u8L("Measure gizmo editing"); } + std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); } protected: bool on_init() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 67eccd8e8..ee32c4596 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -3,6 +3,8 @@ #include "GLGizmoPainterBase.hpp" +#include "slic3r/GUI/I18N.hpp" + namespace Slic3r::GUI { class GLMmSegmentationGizmo3DScene @@ -117,7 +119,7 @@ protected: std::string get_gizmo_entering_text() const override { return _u8L("Entering Multimaterial painting"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Multimaterial painting"); } - std::string get_action_snapshot_name() override { return _u8L("Multimaterial painting editing"); } + std::string get_action_snapshot_name() const override { return _u8L("Multimaterial painting editing"); } size_t m_first_selected_extruder_idx = 0; size_t m_second_selected_extruder_idx = 1; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 98f183b74..73615b463 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoMove.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 3d88ad500..aa3d56ef9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoPainterBase.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 345d733af..75b80d8f5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoRotate.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 9e5191f65..063dce721 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoScale.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp index 533683237..c1a6d7dfa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp @@ -3,6 +3,8 @@ #include "GLGizmoPainterBase.hpp" +#include "slic3r/GUI/I18N.hpp" + namespace Slic3r::GUI { class GLGizmoSeam : public GLGizmoPainterBase @@ -22,7 +24,7 @@ protected: std::string get_gizmo_entering_text() const override { return _u8L("Entering Seam painting"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Seam painting"); } - std::string get_action_snapshot_name() override { return _u8L("Paint-on seam editing"); } + std::string get_action_snapshot_name() const override { return _u8L("Paint-on seam editing"); } private: bool on_init() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index f95aad495..1a56b7791 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -1,10 +1,9 @@ #ifndef slic3r_GLGizmoSimplify_hpp_ #define slic3r_GLGizmoSimplify_hpp_ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, -// which overrides our localization "L" macro. #include "GLGizmoBase.hpp" #include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/I18N.hpp" #include "admesh/stl.h" // indexed_triangle_set #include #include diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 4d76dfb04..18613cd8f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -1,5 +1,4 @@ #include "libslic3r/libslic3r.h" -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoSlaSupports.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/Utils/UndoRedo.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 578858b5b..bb0513682 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -3,6 +3,7 @@ #include "GLGizmoSlaBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" +#include "slic3r/GUI/I18N.hpp" #include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/ObjectID.hpp" diff --git a/src/slic3r/GUI/OG_CustomCtrl.hpp b/src/slic3r/GUI/OG_CustomCtrl.hpp index 0308322f7..269f847a2 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.hpp +++ b/src/slic3r/GUI/OG_CustomCtrl.hpp @@ -11,7 +11,6 @@ #include "libslic3r/PrintConfig.hpp" #include "OptionsGroup.hpp" -#include "I18N.hpp" // Translate the ifdef #ifdef __WXOSX__ From 73857703b8df3fcccd9dad28f0734d1cd9dec38b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Mar 2023 09:21:12 +0100 Subject: [PATCH 140/201] Added a check that libslic3r/I18N.hpp is not included on the frontend by mistake, also added a check that L macro is not defined when including libslic3r/I18N.hpp --- src/libslic3r/I18N.hpp | 25 +++++++++++++++++++------ src/slic3r/CMakeLists.txt | 3 +++ src/slic3r/GUI/GUI_App.cpp | 4 ++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/I18N.hpp b/src/libslic3r/I18N.hpp index 3bf286b19..51d41a3ea 100644 --- a/src/libslic3r/I18N.hpp +++ b/src/libslic3r/I18N.hpp @@ -3,6 +3,12 @@ #include +#ifdef SLIC3R_CURRENTLY_COMPILING_GUI_MODULE + #ifndef SLIC3R_ALLOW_LIBSLIC3R_I18N_IN_SLIC3R + #error You included libslic3r/I18N.hpp into a file belonging to slic3r module. + #endif +#endif + namespace Slic3r { namespace I18N { @@ -15,11 +21,18 @@ namespace I18N { } // namespace Slic3r -namespace { - const char* L(const char* s) { return s; } - std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } - std::string _utf8(const char* s) { return Slic3r::I18N::translate(s); } - std::string _utf8(const std::string& s) { return Slic3r::I18N::translate(s); } -} +// When this is included from slic3r, better do not define the translation functions. +// Macros from slic3r/GUI/I18N.hpp should be used there. +#ifndef SLIC3R_CURRENTLY_COMPILING_GUI_MODULE + #ifdef L + #error L macro is defined where it shouldn't be. Didn't you include slic3r/GUI/I18N.hpp in libslic3r by mistake? + #endif + namespace { + const char* L(const char* s) { return s; } + std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } + std::string _utf8(const char* s) { return Slic3r::I18N::translate(s); } + std::string _utf8(const std::string& s) { return Slic3r::I18N::translate(s); } + } +#endif #endif /* slic3r_I18N_hpp_ */ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 2ca1998e3..db8cefa99 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -344,3 +344,6 @@ if (UNIX AND NOT APPLE) target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS}) target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig) endif () + +# Add a definition so that we can tell we are compiling slic3r. +target_compile_definitions(libslic3r_gui PRIVATE SLIC3R_CURRENTLY_COMPILING_GUI_MODULE) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 5d3b65632..4be6de6e1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -8,7 +8,11 @@ // Localization headers: include libslic3r version first so everything in this file // uses the slic3r/GUI version (the macros will take precedence over the functions). +// Also, there is a check that the former is not included from slic3r module. +// This is the only place where we want to allow that, so define an override macro. +#define SLIC3R_ALLOW_LIBSLIC3R_I18N_IN_SLIC3R #include "libslic3r/I18N.hpp" +#undef SLIC3R_ALLOW_LIBSLIC3R_I18N_IN_SLIC3R #include "slic3r/GUI/I18N.hpp" #include From 472090a54df9a6a5880805bcbe00d59813976503 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Mar 2023 09:37:12 +0100 Subject: [PATCH 141/201] Fix: file containing localization was missing in list.txt --- resources/localization/list.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 1c67fb49f..b239c8d9a 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -113,3 +113,4 @@ src/libslic3r/PrintBase.cpp src/libslic3r/PrintConfig.cpp src/libslic3r/Zipper.cpp src/libslic3r/PrintObject.cpp +src/libslic3r/PrintObjectSlice.cpp From f9de995f9e6b8d64efde122563abee67148a1fb7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 17 Mar 2023 09:28:13 +0100 Subject: [PATCH 142/201] Remove '_utf8' makro. (Initially, it was used because of using of boost::format for localized lines. But now It's better to use format and formal_wxstr functions) + Fixed missed marks for localization + Added missed filed to localization to the list.txt --- resources/localization/list.txt | 2 ++ src/libslic3r/AppConfig.cpp | 6 ---- src/libslic3r/I18N.hpp | 2 -- src/libslic3r/Zipper.cpp | 4 --- src/slic3r/GUI/AboutDialog.cpp | 33 +++++++++--------- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 36 +++++++++---------- src/slic3r/GUI/BonjourDialog.cpp | 6 ++-- src/slic3r/GUI/ConfigSnapshotDialog.cpp | 4 +-- src/slic3r/GUI/ConfigWizard.cpp | 20 +++++------ src/slic3r/GUI/DoubleSlider.cpp | 2 +- src/slic3r/GUI/Field.cpp | 11 +++--- src/slic3r/GUI/GLCanvas3D.cpp | 38 ++++++++++----------- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/GUI_Factories.cpp | 6 ++-- src/slic3r/GUI/GUI_ObjectSettings.cpp | 4 +-- src/slic3r/GUI/HintNotification.cpp | 8 ++--- src/slic3r/GUI/I18N.hpp | 1 - src/slic3r/GUI/Jobs/RotoptimizeJob.cpp | 10 ++++++ src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 10 ++---- src/slic3r/GUI/Jobs/SLAImportDialog.hpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 4 +-- src/slic3r/GUI/NotificationManager.cpp | 4 +-- src/slic3r/GUI/OpenGLManager.cpp | 17 +++++---- src/slic3r/GUI/Plater.cpp | 22 ++++++------ src/slic3r/GUI/PresetHints.cpp | 36 +++++++++---------- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 18 +++++----- src/slic3r/GUI/UpdateDialogs.cpp | 11 +++--- src/slic3r/GUI/wxExtensions.cpp | 5 ++- src/slic3r/Utils/AppUpdater.cpp | 22 ++++++------ src/slic3r/Utils/AstroBox.cpp | 10 +++--- src/slic3r/Utils/Duet.cpp | 4 +-- src/slic3r/Utils/FlashAir.cpp | 8 ++--- src/slic3r/Utils/MKS.cpp | 4 +-- src/slic3r/Utils/OctoPrint.cpp | 26 ++++++-------- src/slic3r/Utils/Repetier.cpp | 14 ++++---- 36 files changed, 195 insertions(+), 219 deletions(-) diff --git a/resources/localization/list.txt b/resources/localization/list.txt index b239c8d9a..875e92721 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -87,6 +87,7 @@ src/slic3r/GUI/UpdateDialogs.cpp src/slic3r/GUI/WipeTowerDialog.cpp src/slic3r/GUI/wxExtensions.cpp src/slic3r/Utils/AstroBox.cpp +src/slic3r/Utils/AppUpdater.cpp src/slic3r/Utils/Duet.cpp src/slic3r/Utils/FixModelByWin10.cpp src/slic3r/Utils/FlashAir.cpp @@ -101,6 +102,7 @@ src/libslic3r/ExtrusionEntity.cpp src/libslic3r/Flow.cpp src/libslic3r/Format/3mf.cpp src/libslic3r/Format/AMF.cpp +src/libslic3r/Format/SLAArchiveReader.cpp src/libslic3r/GCode/PostProcessor.cpp src/libslic3r/miniz_extension.cpp src/libslic3r/Preset.cpp diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index be16a2015..f88b80c92 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -340,12 +340,6 @@ std::string AppConfig::load(const std::string &path) // Error while parsing config file. We'll customize the error message and rethrow to be displayed. // ! But to avoid the use of _utf8 (related to use of wxWidgets) // we will rethrow this exception from the place of load() call, if returned value wouldn't be empty - /* - throw Slic3r::RuntimeError( - _utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. " - "Try to manually delete the file to recover from the error. Your user profiles will not be affected.")) + - "\n\n" + AppConfig::config_path() + "\n\n" + ex.what()); - */ return ex.what(); } } diff --git a/src/libslic3r/I18N.hpp b/src/libslic3r/I18N.hpp index 51d41a3ea..ee39df2e1 100644 --- a/src/libslic3r/I18N.hpp +++ b/src/libslic3r/I18N.hpp @@ -30,8 +30,6 @@ namespace I18N { namespace { const char* L(const char* s) { return s; } std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } - std::string _utf8(const char* s) { return Slic3r::I18N::translate(s); } - std::string _utf8(const std::string& s) { return Slic3r::I18N::translate(s); } } #endif diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index 29ca264ca..53c39c3a7 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -6,10 +6,6 @@ #include #include "I18N.hpp" -//! macro used to mark string used at localization, -//! return same string -#define _u8L(s) Slic3r::I18N::translate(s) - #if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L #define SLIC3R_NORETURN #elif __cplusplus >= 201103L diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 823a315b7..132159ff5 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -41,9 +41,9 @@ void AboutDialogLogo::onRepaint(wxEvent &event) // CopyrightsDialog // ----------------------------------------- CopyrightsDialog::CopyrightsDialog() - : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, from_u8((boost::format("%1% - %2%") - % (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME) - % _utf8(L("Portions copyright"))).str()), + : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, format_wxstr("%1% - %2%" + , wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME + , _L("Portions copyright")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { this->SetFont(wxGetApp().normal_font()); @@ -211,7 +211,7 @@ void CopyrightsDialog::onCloseDialog(wxEvent &) } AboutDialog::AboutDialog() - : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME)).str()), wxDefaultPosition, + : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, format_wxstr(_L("About %s"), wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME), wxDefaultPosition, wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { SetFont(wxGetApp().normal_font()); @@ -267,14 +267,13 @@ AboutDialog::AboutDialog() int size[] = {fs,fs,fs,fs,fs,fs,fs}; m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); m_html->SetBorders(2); - const std::string copyright_str = _utf8(L("Copyright")); + const wxString copyright_str = _L("Copyright"); // TRN "Slic3r _is licensed under the_ License" - const std::string is_lecensed_str = _utf8(L("is licensed under the")); - const std::string license_str = _utf8(L("GNU Affero General Public License, version 3")); - const std::string based_on_str = _utf8(L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.")); - const std::string contributors_str = _utf8(L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others.")); - const auto text = from_u8( - (boost::format( + const wxString is_lecensed_str = _L("is licensed under the"); + const wxString license_str = _L("GNU Affero General Public License, version 3"); + const wxString based_on_str = _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community."); + const wxString contributors_str = _L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."); + const auto text = format_wxstr( "" "" "" @@ -288,12 +287,12 @@ AboutDialog::AboutDialog() "%9%" "" "" - "") % bgr_clr_str % text_clr_str % text_clr_str - % copyright_str % copyright_str - % is_lecensed_str - % license_str - % based_on_str - % contributors_str).str()); + "", bgr_clr_str, text_clr_str, text_clr_str + , copyright_str, copyright_str + , is_lecensed_str + , license_str + , based_on_str + , contributors_str); m_html->SetPage(text); vsizer->Add(m_html, 1, wxEXPAND | wxBOTTOM, 10); m_html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 234825508..9ee6946c6 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -76,10 +76,10 @@ std::pair SlicingProcessCompletedEvent::format_error_message( try { this->rethrow_exception(); } catch (const std::bad_alloc &ex) { - wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. " + error = GUI::format(_L("%s has encountered an error. It was likely caused by running out of memory. " "If you are sure you have enough RAM on your system, this may also be a bug and we would " - "be glad if you reported it."))) % SLIC3R_APP_NAME).str()); - error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what()); + "be glad if you reported it."), SLIC3R_APP_NAME); + error += "\n\n" + std::string(ex.what()); } catch (const HardCrash &ex) { error = GUI::format(_L("PrusaSlicer has encountered a fatal error: \"%1%\""), ex.what()) + "\n\n" + _u8L("Please save your project and restart PrusaSlicer. " @@ -159,7 +159,7 @@ void BackgroundSlicingProcess::process_fff() wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); prepare_upload(); } else { - m_print->set_status(100, _utf8(L("Slicing complete"))); + m_print->set_status(100, _u8L("Slicing complete")); } this->set_step_done(bspsGCodeFinalize); } @@ -180,12 +180,12 @@ void BackgroundSlicingProcess::process_sla() m_sla_print->export_print(export_path, thumbnails); - m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str()); + m_print->set_status(100, GUI::format(_L("Masked SLA file exported to %1%"), export_path)); } else if (! m_upload_job.empty()) { wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); prepare_upload(); } else { - m_print->set_status(100, _utf8(L("Slicing complete"))); + m_print->set_status(100, _u8L("Slicing complete")); } this->set_step_done(bspsGCodeFinalize); } @@ -649,7 +649,7 @@ bool BackgroundSlicingProcess::invalidate_all_steps() // Copy the final G-code to target location (possibly a SD card, if it is a removable media, then verify that the file was written without an error). void BackgroundSlicingProcess::finalize_gcode() { - m_print->set_status(95, _utf8(L("Running post-processing scripts"))); + m_print->set_status(95, _u8L("Running post-processing scripts")); // Perform the final post-processing of the export path by applying the print statistics over the file name. std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); @@ -680,32 +680,32 @@ void BackgroundSlicingProcess::finalize_gcode() catch (...) { remove_post_processed_temp_file(); - throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code."))); + throw Slic3r::ExportError(_u8L("Unknown error occured during exporting G-code.")); } switch (copy_ret_val) { case CopyFileResult::SUCCESS: break; // no error case CopyFileResult::FAIL_COPY_FILE: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str()); + throw Slic3r::ExportError(GUI::format(_L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"), error_message)); break; case CopyFileResult::FAIL_FILES_DIFFERENT: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); + throw Slic3r::ExportError(GUI::format(_L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."), export_path)); break; case CopyFileResult::FAIL_RENAMING: - throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); + throw Slic3r::ExportError(GUI::format(_L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."), export_path)); break; case CopyFileResult::FAIL_CHECK_ORIGIN_NOT_OPENED: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % output_path % export_path).str()); + throw Slic3r::ExportError(GUI::format(_L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."), output_path, export_path)); break; case CopyFileResult::FAIL_CHECK_TARGET_NOT_OPENED: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); + throw Slic3r::ExportError(GUI::format(_L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."), export_path)); break; default: - throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code."))); + throw Slic3r::ExportError(_u8L("Unknown error occured during exporting G-code.")); BOOST_LOG_TRIVIAL(error) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << "."; break; } - m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); + m_print->set_status(100, GUI::format(_L("G-code file exported to %1%"), export_path)); } // A print host upload job has been scheduled, enqueue it to the printhost job queue @@ -716,10 +716,10 @@ void BackgroundSlicingProcess::prepare_upload() / boost::filesystem::unique_path("." SLIC3R_APP_KEY ".upload.%%%%-%%%%-%%%%-%%%%"); if (m_print == m_fff_print) { - m_print->set_status(95, _utf8(L("Running post-processing scripts"))); + m_print->set_status(95, _u8L("Running post-processing scripts")); std::string error_message; if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS) - throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); + throw Slic3r::RuntimeError(_u8L("Copying of the temporary G-code to the output G-code failed")); m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); // Make a copy of the source path, as run_post_process_scripts() is allowed to change it when making a copy of the source file // (not here, but when the final target is a file). @@ -735,7 +735,7 @@ void BackgroundSlicingProcess::prepare_upload() m_sla_print->export_print(source_path.string(),thumbnails, m_upload_job.upload_data.upload_path.filename().string()); } - m_print->set_status(100, (boost::format(_utf8(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"))) % m_upload_job.printhost->get_host()).str()); + m_print->set_status(100, GUI::format(_L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"), m_upload_job.printhost->get_host())); m_upload_job.upload_data.source_path = std::move(source_path); diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index 9298aa615..060643c1f 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -224,14 +224,14 @@ void BonjourDialog::on_timer(wxTimerEvent &) // explicitly (wxTimerEvent should not be created by user code). void BonjourDialog::on_timer_process() { - const auto search_str = _utf8(L("Searching for devices")); + const auto search_str = _L("Searching for devices"); if (timer_state > 0) { const std::string dots(timer_state, '.'); - label->SetLabel(GUI::from_u8((boost::format("%1% %2%") % search_str % dots).str())); + label->SetLabel(search_str + dots); timer_state = (timer_state) % 3 + 1; } else { - label->SetLabel(GUI::from_u8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str())); + label->SetLabel(search_str + ": " + _L("Finished") + "."); timer->Stop(); } } diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index e2bea55d1..c1817a3ba 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -89,10 +89,10 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve } if (! compatible) { - text += "

" + from_u8((boost::format(_utf8(L("Incompatible with this %s"))) % SLIC3R_APP_NAME).str()) + "

"; + text += "

" + format_wxstr(_L("Incompatible with this %s"), SLIC3R_APP_NAME) + "

"; } else if (! snapshot_active) - text += "

" + _(L("Activate")) + "

"; + text += "

" + _L("Activate") + "

"; text += ""; text += ""; return text; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 65d08f195..aa2de94af 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -289,7 +289,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt const auto &variant = model.variants[i]; const auto label = model.technology == ptFFF - ? from_u8((boost::format("%1% %2% %3%") % variant.name % _utf8(L("mm")) % _utf8(L("nozzle"))).str()) + ? format_wxstr("%1% %2% %3%", variant.name, _L("mm"), _L("nozzle")) : from_u8(model.name); if (i == 1) { @@ -509,17 +509,17 @@ void ConfigWizardPage::append_spacer(int space) // Wizard pages PageWelcome::PageWelcome(ConfigWizard *parent) - : ConfigWizardPage(parent, from_u8((boost::format( + : ConfigWizardPage(parent, format_wxstr( #ifdef __APPLE__ - _utf8(L("Welcome to the %s Configuration Assistant")) + _L("Welcome to the %s Configuration Assistant") #else - _utf8(L("Welcome to the %s Configuration Wizard")) + _L("Welcome to the %s Configuration Wizard") #endif - ) % SLIC3R_APP_NAME).str()), _L("Welcome")) - , welcome_text(append_text(from_u8((boost::format( - _utf8(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print."))) - % SLIC3R_APP_NAME - % _utf8(ConfigWizard::name())).str()) + , SLIC3R_APP_NAME), _L("Welcome")) + , welcome_text(append_text(format_wxstr( + _L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.") + , SLIC3R_APP_NAME + , _(ConfigWizard::name())) )) , cbox_reset(append( new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)")) @@ -577,7 +577,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent, continue; } - const auto picker_title = family.empty() ? wxString() : from_u8((boost::format(_utf8(L("%s Family"))) % family).str()); + const auto picker_title = family.empty() ? wxString() : format_wxstr(_L("%s Family"), family); auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, *appconfig, filter); picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) { diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 99b8d73d5..a0816c22f 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -106,7 +106,7 @@ Control::Control( wxWindow *parent, m_cog_icon_dim = m_bmp_cog.GetWidth(); m_selection = ssUndef; - m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume printing"))); + m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); m_ticks.set_extruder_colors(&m_extruder_colors); // slider events diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index c6ffa46ec..74ad028d3 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -226,7 +226,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true } wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); - show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str())); + show_error(m_parent, format_wxstr(_L("%s doesn't support percentage"), label)); set_value(double_to_string(m_opt.min), true); m_value = double(m_opt.min); break; @@ -299,7 +299,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true // Workaroud to avoid of using of the % for first layer height // see https://github.com/prusa3d/PrusaSlicer/issues/7418 wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); - show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str())); + show_error(m_parent, format_wxstr(_L("%s doesn't support percentage"), label)); const wxString stVal = double_to_string(0.01, 2); set_value(stVal, true); m_value = into_u8(stVal);; @@ -341,9 +341,10 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; const wxString stVal = double_to_string(val, 2); - const wxString msg_text = from_u8((boost::format(_utf8(L("Do you mean %s%% instead of %s %s?\n" - "Select YES if you want to change this value to %s%%, \n" - "or NO if you are sure that %s %s is a correct value."))) % stVal % stVal % sidetext % stVal % stVal % sidetext).str()); + // TRN %1% = Value, %2% = units + const wxString msg_text = format_wxstr(_L("Do you mean %1%%% instead of %1% %2%?\n" + "Select YES if you want to change this value to %1%%%, \n" + "or NO if you are sure that %1% %2% is a correct value."), stVal, sidetext); WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxYES | wxNO); if ((!infill_anchors || val > 100) && dialog.ShowModal() == wxID_YES) { set_value(from_u8((boost::format("%s%%") % stVal).str()), false/*true*/); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6924667e6..dcc85dc65 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -728,7 +728,7 @@ void GLCanvas3D::Labels::render(const std::vector& sorted_ return owner.model_instance_id == id; }); if (it != owners.end()) - it->print_order = std::string((_(L("Seq."))).ToUTF8()) + "#: " + std::to_string(i + 1); + it->print_order = _u8L("Seq.") + "#: " + std::to_string(i + 1); } } @@ -4711,7 +4711,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "add"; item.icon_filename = "add.svg"; - item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; + item.tooltip = _u8L("Add...") + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; if (!m_main_toolbar.add_item(item)) @@ -4719,7 +4719,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "delete"; item.icon_filename = "remove.svg"; - item.tooltip = _utf8(L("Delete")) + " [Del]"; + item.tooltip = _u8L("Delete") + " [Del]"; item.sprite_id = 1; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; @@ -4728,7 +4728,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "deleteall"; item.icon_filename = "delete_all.svg"; - item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; + item.tooltip = _u8L("Delete all") + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; item.sprite_id = 2; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; @@ -4737,7 +4737,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "arrange"; item.icon_filename = "arrange.svg"; - item.tooltip = _utf8(L("Arrange")) + " [A]\n" + _utf8(L("Arrange selection")) + " [Shift+A]\n" + _utf8(L("Click right mouse button to show arrangement options")); + item.tooltip = _u8L("Arrange") + " [A]\n" + _u8L("Arrange selection") + " [Shift+A]\n" + _u8L("Click right mouse button to show arrangement options"); item.sprite_id = 3; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; @@ -4757,7 +4757,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "copy"; item.icon_filename = "copy.svg"; - item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]"; + item.tooltip = _u8L("Copy") + " [" + GUI::shortkey_ctrl_prefix() + "C]"; item.sprite_id = 4; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); }; @@ -4766,7 +4766,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "paste"; item.icon_filename = "paste.svg"; - item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]"; + item.tooltip = _u8L("Paste") + " [" + GUI::shortkey_ctrl_prefix() + "V]"; item.sprite_id = 5; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); }; @@ -4778,7 +4778,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "more"; item.icon_filename = "instance_add.svg"; - item.tooltip = _utf8(L("Add instance")) + " [+]"; + item.tooltip = _u8L("Add instance") + " [+]"; item.sprite_id = 6; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; @@ -4789,7 +4789,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "fewer"; item.icon_filename = "instance_remove.svg"; - item.tooltip = _utf8(L("Remove instance")) + " [-]"; + item.tooltip = _u8L("Remove instance") + " [-]"; item.sprite_id = 7; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; @@ -4802,7 +4802,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "splitobjects"; item.icon_filename = "split_objects.svg"; - item.tooltip = _utf8(L("Split to objects")); + item.tooltip = _u8L("Split to objects"); item.sprite_id = 8; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; @@ -4812,7 +4812,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "splitvolumes"; item.icon_filename = "split_parts.svg"; - item.tooltip = _utf8(L("Split to parts")); + item.tooltip = _u8L("Split to parts"); item.sprite_id = 9; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; @@ -4826,8 +4826,8 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "settings"; item.icon_filename = "settings.svg"; item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + - "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + - "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab")) ; item.sprite_id = 10; item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; item.visibility_callback = []() { return wxGetApp().app_config->get_bool("new_settings_layout_mode") || @@ -4843,7 +4843,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "search"; item.icon_filename = "search_.svg"; - item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]"; + item.tooltip = _u8L("Search") + " [" + GUI::shortkey_ctrl_prefix() + "F]"; item.sprite_id = 11; item.left.toggable = true; item.left.render_callback = [this](float left, float right, float, float) { @@ -4865,7 +4865,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "layersediting"; item.icon_filename = "layers_white.svg"; - item.tooltip = _utf8(L("Variable layer height")); + item.tooltip = _u8L("Variable layer height"); item.sprite_id = 12; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { @@ -4918,7 +4918,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() item.name = "undo"; item.icon_filename = "undo_toolbar.svg"; - item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]\n" + _utf8(L("Click right mouse button to open/close History")); + item.tooltip = _u8L("Undo") + " [" + GUI::shortkey_ctrl_prefix() + "Z]\n" + _u8L("Click right mouse button to open/close History"); item.sprite_id = 0; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); }; item.right.toggable = true; @@ -4940,7 +4940,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() if (can_undo) { std::string action; wxGetApp().plater()->undo_redo_topmost_string_getter(true, action); - new_additional_tooltip = (boost::format(_utf8(L("Next Undo action: %1%"))) % action).str(); + new_additional_tooltip = format(_L("Next Undo action: %1%"), action); } if (new_additional_tooltip != curr_additional_tooltip) { @@ -4955,7 +4955,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() item.name = "redo"; item.icon_filename = "redo_toolbar.svg"; - item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]\n" + _utf8(L("Click right mouse button to open/close History")); + item.tooltip = _u8L("Redo") + " [" + GUI::shortkey_ctrl_prefix() + "Y]\n" + _u8L("Click right mouse button to open/close History"); item.sprite_id = 1; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; @@ -4976,7 +4976,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() if (can_redo) { std::string action; wxGetApp().plater()->undo_redo_topmost_string_getter(false, action); - new_additional_tooltip = (boost::format(_utf8(L("Next Redo action: %1%"))) % action).str(); + new_additional_tooltip = format(_L("Next Redo action: %1%"), action); } if (new_additional_tooltip != curr_additional_tooltip) { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4be6de6e1..fe1fd2012 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3440,7 +3440,7 @@ void GUI_App::app_updater(bool from_user) } app_data.target_path =dwnld_dlg.get_download_path(); // start download - this->plater_->get_notification_manager()->push_download_progress_notification(GUI::format(_utf8("Downloading %1%"), app_data.target_path.filename().string()), std::bind(&AppUpdater::cancel_callback, this->m_app_updater.get())); + this->plater_->get_notification_manager()->push_download_progress_notification(GUI::format(_L("Downloading %1%"), app_data.target_path.filename().string()), std::bind(&AppUpdater::cancel_callback, this->m_app_updater.get())); app_data.start_after = dwnld_dlg.run_after_download(); m_app_updater->set_app_data(std::move(app_data)); m_app_updater->sync_download(); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index df8ace79a..4887d3dcf 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -411,7 +411,7 @@ static void create_freq_settings_popupmenu(wxMenu* menu, const bool is_object_se if (is_improper_category(category.first, extruders_cnt)) continue; - append_menu_item(menu, wxID_ANY, from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()), "", + append_menu_item(menu, wxID_ANY, format_wxstr(_L("Quick Add Settings (%s)"), _(it.first)), "", [menu, item, is_object_settings, bundle](wxCommandEvent& event) { wxString category_name = menu->GetLabel(event.GetId()); std::vector options; @@ -622,13 +622,13 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) #if 0 for (auto& it : m_freq_settings_fff) { - settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str())); + settings_id = menu->FindItem(format_wxstr(_L("Quick Add Settings (%s)"), _(it.first))); if (settings_id != wxNOT_FOUND) menu->Destroy(settings_id); } for (auto& it : m_freq_settings_sla) { - settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str())); + settings_id = menu->FindItem(format_wxstr(_L("Quick Add Settings (%s)"), _(it.first))); if (settings_id != wxNOT_FOUND) menu->Destroy(settings_id); } diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 97eb5f10d..41cad8792 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -102,7 +102,7 @@ bool ObjectSettings::update_settings_list() btn->SetBitmapCurrent(m_bmp_delete_focus.bmp()); btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { - wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Delete Option %s"))) % opt_key).str())); + wxGetApp().plater()->take_snapshot(format_wxstr(_L("Delete Option %s"), opt_key)); config->erase(opt_key); wxGetApp().obj_list()->changed_object(); wxTheApp->CallAfter([this]() { @@ -151,7 +151,7 @@ bool ObjectSettings::update_settings_list() for (auto& opt : cat.second) optgroup->get_field(opt)->m_on_change = [optgroup](const std::string& opt_id, const boost::any& value) { // first of all take a snapshot and then change value in configuration - wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Change Option %s"))) % opt_id).str())); + wxGetApp().plater()->take_snapshot(format_wxstr(_L("Change Option %s"), opt_id)); optgroup->on_change_OG(opt_id, value); }; diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 1f3d22491..95583a3cd 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -344,7 +344,7 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) bool was_displayed = is_used(id_string); //unescape text1 unescape_string_cstyle(dict["text"], fulltext); - fulltext = _utf8(fulltext); + fulltext = into_u8(_(fulltext)); #ifdef __APPLE__ boost::replace_all(fulltext, "Ctrl+", "⌘"); #endif //__APPLE__ @@ -370,19 +370,19 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) fulltext.erase(hypertext_start, HYPERTEXT_MARKER_START.size()); if (fulltext.find(HYPERTEXT_MARKER_START) != std::string::npos) { // This must not happen - only 1 hypertext allowed - BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertexts: " << _utf8(dict["text"]); + BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertexts: " << dict["text"]; continue; } size_t hypertext_end = fulltext.find(HYPERTEXT_MARKER_END); if (hypertext_end == std::string::npos) { // hypertext was not correctly ended - BOOST_LOG_TRIVIAL(error) << "Hint notification without hypertext end marker: " << _utf8(dict["text"]); + BOOST_LOG_TRIVIAL(error) << "Hint notification without hypertext end marker: " << dict["text"]; continue; } fulltext.erase(hypertext_end, HYPERTEXT_MARKER_END.size()); if (fulltext.find(HYPERTEXT_MARKER_END) != std::string::npos) { // This must not happen - only 1 hypertext end allowed - BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertext end markers: " << _utf8(dict["text"]); + BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertext end markers: " << dict["text"]; continue; } diff --git a/src/slic3r/GUI/I18N.hpp b/src/slic3r/GUI/I18N.hpp index 7bad6880e..8616628ea 100644 --- a/src/slic3r/GUI/I18N.hpp +++ b/src/slic3r/GUI/I18N.hpp @@ -1,7 +1,6 @@ #ifndef _ #define _(s) Slic3r::GUI::I18N::translate((s)) #define _L(s) Slic3r::GUI::I18N::translate((s)) -#define _utf8(s) Slic3r::GUI::I18N::translate_utf8((s)) #define _u8L(s) Slic3r::GUI::I18N::translate_utf8((s)) #endif /* _ */ diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp index 0980326d3..d33bee1a8 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp @@ -124,4 +124,14 @@ void RotoptimizeJob::finalize(bool canceled, std::exception_ptr &eptr) m_plater->update(); } +std::string RotoptimizeJob::get_method_name(size_t i) +{ + return into_u8(_(Methods[i].name)); +} + +std::string RotoptimizeJob::get_method_description(size_t i) +{ + return into_u8(_(Methods[i].descr)); +} + }} diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index 71a28deb7..02aafb551 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -58,15 +58,9 @@ public: static constexpr size_t get_methods_count() { return std::size(Methods); } - static std::string get_method_name(size_t i) - { - return _utf8(Methods[i].name); - } + static std::string get_method_name(size_t i); - static std::string get_method_description(size_t i) - { - return _utf8(Methods[i].descr); - } + static std::string get_method_description(size_t i); }; }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/SLAImportDialog.hpp b/src/slic3r/GUI/Jobs/SLAImportDialog.hpp index 5477e51e7..fed84600c 100644 --- a/src/slic3r/GUI/Jobs/SLAImportDialog.hpp +++ b/src/slic3r/GUI/Jobs/SLAImportDialog.hpp @@ -30,7 +30,7 @@ std::string get_readers_wildcard() std::string ret; for (const char *archtype : SLAArchiveReader::registered_archives()) { - ret += _utf8(SLAArchiveReader::get_description(archtype)); + ret += into_u8(_(SLAArchiveReader::get_description(archtype))); ret += " ("; auto extensions = SLAArchiveReader::get_extensions(archtype); for (const char * ext : extensions) { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index eccd96e20..071260ac2 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1755,7 +1755,7 @@ void MainFrame::quick_slice(const int qs) } else if (qs & qsSaveAs) { // The following line may die if the output_filename_format template substitution fails. - wxFileDialog dlg(this, from_u8((boost::format(_utf8(L("Save %s file as:"))) % ((qs & qsExportSVG) ? _L("SVG") : _L("G-code"))).str()), + wxFileDialog dlg(this, format_wxstr(_L("Save %s file as:"), ((qs & qsExportSVG) ? _L("SVG") : _L("G-code"))), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); @@ -1778,7 +1778,7 @@ void MainFrame::quick_slice(const int qs) // show processbar dialog m_progress_dialog = new wxProgressDialog(_L("Slicing") + dots, // TRN "Processing input_file_basename" - from_u8((boost::format(_utf8(L("Processing %s"))) % (input_file_basename + dots)).str()), + format_wxstr(_L("Processing %s"), (input_file_basename + dots)), 100, nullptr, wxPD_AUTO_HIDE); m_progress_dialog->Pulse(); { diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index ab465a577..c96ed4dbc 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -866,7 +866,7 @@ bool NotificationManager::ExportFinishedNotification::on_text_click() } void NotificationManager::ExportFinishedNotification::on_eject_click() { - NotificationData data{ get_data().type, get_data().level , 0, _utf8("Ejecting.") }; + NotificationData data{ get_data().type, get_data().level , 0, _u8L("Ejecting.") }; m_eject_pending = true; m_multiline = false; update(data); @@ -2447,7 +2447,7 @@ void NotificationManager::push_download_URL_progress_notification(size_t id, con } } // push new one - NotificationData data{ NotificationType::URLDownload, NotificationLevel::ProgressBarNotificationLevel, 5, _utf8("Download:") + " " + text }; + NotificationData data{ NotificationType::URLDownload, NotificationLevel::ProgressBarNotificationLevel, 5, _u8L("Download:") + " " + text }; push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, id, user_action_callback), 0); } diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index ecf0c5790..6960d0a75 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -371,16 +371,16 @@ bool OpenGLManager::init_gl() if (!valid_version) { // Complain about the OpenGL version. - wxString message = from_u8((boost::format( + wxString message = format_wxstr( #if ENABLE_OPENGL_ES - _utf8(L("PrusaSlicer requires OpenGL ES 2.0 capable graphics driver to run correctly, \n" - "while OpenGL version %s, render %s, vendor %s was detected."))) % s_gl_info.get_version_string() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str()); + _L("PrusaSlicer requires OpenGL ES 2.0 capable graphics driver to run correctly, \n" + "while OpenGL version %s, render %s, vendor %s was detected."), s_gl_info.get_version_string(), s_gl_info.get_renderer(), s_gl_info.get_vendor()); #elif ENABLE_GL_CORE_PROFILE - _utf8(L("PrusaSlicer requires OpenGL %s capable graphics driver to run correctly, \n" - "while OpenGL version %s, render %s, vendor %s was detected."))) % (s_gl_info.is_core_profile() ? "3.3" : "2.0") % s_gl_info.get_version_string() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str()); + _L("PrusaSlicer requires OpenGL %s capable graphics driver to run correctly, \n" + "while OpenGL version %s, render %s, vendor %s was detected."), (s_gl_info.is_core_profile() ? "3.3" : "2.0"), s_gl_info.get_version_string(), s_gl_info.get_renderer(), s_gl_info.get_vendor()); #else - _utf8(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" - "while OpenGL version %s, render %s, vendor %s was detected."))) % s_gl_info.get_version_string() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str()); + _L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" + "while OpenGL version %s, render %s, vendor %s was detected."), s_gl_info.get_version_string(), s_gl_info.get_renderer(), s_gl_info.get_vendor()); #endif // ENABLE_OPENGL_ES message += "\n"; message += _L("You may need to update your graphics card driver."); @@ -395,8 +395,7 @@ bool OpenGLManager::init_gl() // load shaders auto [result, error] = m_shaders_manager.init(); if (!result) { - wxString message = from_u8((boost::format( - _utf8(L("Unable to load the following shaders:\n%s"))) % error).str()); + wxString message = format_wxstr(_L("Unable to load the following shaders:\n%s"), error); wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Error loading shaders"), wxOK | wxICON_ERROR); } #if ENABLE_OPENGL_DEBUG_OPTION diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6a17c4ee5..08067c568 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1376,7 +1376,7 @@ void Sidebar::update_sliced_info_sizer() wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time)); p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _L("Estimated printing time") + ":"); - p->plater->get_notification_manager()->set_slicing_complete_print_time(_utf8("Estimated printing time: ") + boost::nowide::narrow(t_est), p->plater->is_sidebar_collapsed()); + p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + boost::nowide::narrow(t_est), p->plater->is_sidebar_collapsed()); // Hide non-SLA sliced info parameters p->sliced_info->SetTextAndShow(siFilament_m, "N/A"); @@ -1466,7 +1466,7 @@ void Sidebar::update_sliced_info_sizer() new_label += format_wxstr("\n - %1%", _L("normal mode")); info_text += format_wxstr("\n%1%", short_time(ps.estimated_normal_print_time)); - p->plater->get_notification_manager()->set_slicing_complete_print_time(_utf8("Estimated printing time: ") + ps.estimated_normal_print_time, p->plater->is_sidebar_collapsed()); + p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + ps.estimated_normal_print_time, p->plater->is_sidebar_collapsed()); } if (ps.estimated_silent_print_time != "N/A") { @@ -2343,8 +2343,8 @@ void Plater::priv::collapse_sidebar(bool collapse) // Now update the tooltip in the toolbar. std::string new_tooltip = collapse - ? _utf8(L("Expand sidebar")) - : _utf8(L("Collapse sidebar")); + ? _u8L("Expand sidebar") + : _u8L("Collapse sidebar"); new_tooltip += " [Shift+Tab]"; int id = collapse_toolbar.get_item_id("collapse_sidebar"); collapse_toolbar.set_tooltip(id, new_tooltip); @@ -4251,7 +4251,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) } if (evt.cancelled()) { // this->statusbar()->set_status_text(_L("Cancelled")); - this->notification_manager->set_slicing_progress_canceled(_utf8("Slicing Cancelled.")); + this->notification_manager->set_slicing_progress_canceled(_u8L("Slicing Cancelled.")); } this->sidebar->show_sliced_info_sizer(evt.success()); @@ -4558,7 +4558,7 @@ bool Plater::priv::init_view_toolbar() item.name = "3D"; item.icon_filename = "editor.svg"; - item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]"; + item.tooltip = _u8L("3D editor view") + " [" + GUI::shortkey_ctrl_prefix() + "5]"; item.sprite_id = 0; item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; if (!view_toolbar.add_item(item)) @@ -4566,7 +4566,7 @@ bool Plater::priv::init_view_toolbar() item.name = "Preview"; item.icon_filename = "preview.svg"; - item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]"; + item.tooltip = _u8L("Preview") + " [" + GUI::shortkey_ctrl_prefix() + "6]"; item.sprite_id = 1; item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; if (!view_toolbar.add_item(item)) @@ -5437,7 +5437,7 @@ protected: LoadProjectsDialog::LoadProjectsDialog(const std::vector& paths) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, - from_u8((boost::format(_utf8(L("%s - Multiple projects file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition, + format_wxstr(_L("%s - Multiple projects file"), SLIC3R_APP_NAME), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) { SetFont(wxGetApp().normal_font()); @@ -5557,7 +5557,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) mz_zip_zero_struct(&archive); if (!open_zip_reader(&archive, archive_path.string())) { - std::string err_msg = GUI::format(_utf8("Loading of a zip archive on path %1% has failed."), archive_path.string()); + std::string err_msg = GUI::format(_u8L("Loading of a zip archive on path %1% has failed."), archive_path.string()); throw Slic3r::FileIOError(err_msg); } mz_uint num_entries = mz_zip_reader_get_num_files(&archive); @@ -5826,9 +5826,7 @@ protected: ProjectDropDialog::ProjectDropDialog(const std::string& filename) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, -// #ysFIXME_delete_after_test_of_6377 -// from_u8((boost::format(_utf8(L("%s - Drop project file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition, - from_u8((boost::format(_utf8(L("%s - Load project file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition, + format_wxstr(_L("%s - Load project file"), SLIC3R_APP_NAME), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) { SetFont(wxGetApp().normal_font()); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index ce709d9eb..15017ba93 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -163,7 +163,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle double volumetric_flow = flow.mm3_per_mm() * (bridging ? bridge_speed : limit_by_first_layer_speed(speed, max_print_speed)); if (max_flow < volumetric_flow) { max_flow = volumetric_flow; - max_flow_extrusion_type = _utf8(err_msg); + max_flow_extrusion_type = GUI::into_u8(_(err_msg)); } }; if (perimeter_extruder_active) { @@ -184,17 +184,17 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle //FIXME handle gap_fill_speed if (! out.empty()) out += "\n"; - out += (first_layer ? _utf8(L("First layer volumetric")) : (bridging ? _utf8(L("Bridging volumetric")) : _utf8(L("Volumetric")))); - out += " " + _utf8(L("flow rate is maximized")) + " "; + out += (first_layer ? _u8L("First layer volumetric") : (bridging ? _u8L("Bridging volumetric") : _u8L("Volumetric"))); + out += " " + _u8L("flow rate is maximized") + " "; bool limited_by_max_volumetric_speed = max_volumetric_speed > 0 && max_volumetric_speed < max_flow; out += (limited_by_max_volumetric_speed ? - _utf8(L("by the print profile maximum")) : - (_utf8(L("when printing"))+ " " + max_flow_extrusion_type)) - + " " + _utf8(L("with a volumetric rate"))+ " "; + _u8L("by the print profile maximum") : + (_u8L("when printing")+ " " + max_flow_extrusion_type)) + + " " + _u8L("with a volumetric rate")+ " "; if (limited_by_max_volumetric_speed) max_flow = max_volumetric_speed; - out += (boost::format(_utf8(L("%3.2f mm³/s at filament speed %3.2f mm/s."))) % max_flow % (max_flow / filament_crossection)).str(); + out += format(_u8L("%3.2f mm³/s at filament speed %3.2f mm/s."), max_flow, (max_flow / filament_crossection)); } return out; @@ -212,13 +212,13 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre std::string out; if (layer_height <= 0.f) { - out += _utf8(L("Recommended object thin wall thickness: Not available due to invalid layer height.")); + out += _u8L("Recommended object thin wall thickness: Not available due to invalid layer height."); return out; } if (num_perimeters > 0) { int num_lines = std::min(num_perimeters * 2, 10); - out += (boost::format(_utf8(L("Recommended object thin wall thickness for layer height %.2f and"))) % layer_height).str() + " "; + out += (boost::format(_u8L("Recommended object thin wall thickness for layer height %.2f and")) % layer_height).str() + " "; // Start with the width of two closely spaced try { Flow external_perimeter_flow = Flow::new_from_config_width( @@ -233,11 +233,11 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) { if (i > 2) out += ", "; - out += (boost::format(_utf8(L("%d lines: %.2f mm"))) % i % width).str() + " "; + out += (boost::format(_u8L("%d lines: %.2f mm")) % i % width).str() + " "; width += perimeter_flow.spacing() * (thin_walls ? 1.f : 2.f); } } catch (const FlowErrorNegativeSpacing &) { - out = _utf8(L("Recommended object thin wall thickness: Not available due to excessively small extrusion width.")); + out = _u8L("Recommended object thin wall thickness: Not available due to excessively small extrusion width."); } } return out; @@ -266,7 +266,7 @@ std::string PresetHints::top_bottom_shell_thickness_explanation(const PresetBund double min_layer_height = variable_layer_height ? Slicing::min_layer_height_from_nozzle(printer_config, 1) : layer_height; if (layer_height <= 0.f) { - out += _utf8(L("Top / bottom shell thickness hint: Not available due to invalid layer height.")); + out += _u8L("Top / bottom shell thickness hint: Not available due to invalid layer height."); return out; } @@ -279,13 +279,13 @@ std::string PresetHints::top_bottom_shell_thickness_explanation(const PresetBund top_shell_thickness = n * layer_height; } double top_shell_thickness_minimum = std::max(top_solid_min_thickness, top_solid_layers * min_layer_height); - out += (boost::format(_utf8(L("Top shell is %1% mm thick for layer height %2% mm."))) % top_shell_thickness % layer_height).str(); + out += (boost::format(_u8L("Top shell is %1% mm thick for layer height %2% mm.")) % top_shell_thickness % layer_height).str(); if (variable_layer_height && top_shell_thickness_minimum < top_shell_thickness) { out += " "; - out += (boost::format(_utf8(L("Minimum top shell thickness is %1% mm."))) % top_shell_thickness_minimum).str(); + out += (boost::format(_u8L("Minimum top shell thickness is %1% mm.")) % top_shell_thickness_minimum).str(); } } else - out += _utf8(L("Top is open.")); + out += _u8L("Top is open."); out += "\n"; @@ -298,13 +298,13 @@ std::string PresetHints::top_bottom_shell_thickness_explanation(const PresetBund bottom_shell_thickness = n * layer_height; } double bottom_shell_thickness_minimum = std::max(bottom_solid_min_thickness, bottom_solid_layers * min_layer_height); - out += (boost::format(_utf8(L("Bottom shell is %1% mm thick for layer height %2% mm."))) % bottom_shell_thickness % layer_height).str(); + out += (boost::format(_u8L("Bottom shell is %1% mm thick for layer height %2% mm.")) % bottom_shell_thickness % layer_height).str(); if (variable_layer_height && bottom_shell_thickness_minimum < bottom_shell_thickness) { out += " "; - out += (boost::format(_utf8(L("Minimum bottom shell thickness is %1% mm."))) % bottom_shell_thickness_minimum).str(); + out += (boost::format(_u8L("Minimum bottom shell thickness is %1% mm.")) % bottom_shell_thickness_minimum).str(); } } else - out += _utf8(L("Bottom is open.")); + out += _u8L("Bottom is open."); return out; } diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index f986a1552..b381024aa 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -473,7 +473,7 @@ void PrintHostQueueDialog::on_error(Event &evt) set_state(evt.job_id, ST_ERROR); - auto errormsg = from_u8((boost::format("%1%\n%2%") % _utf8(L("Error uploading to print host:")) % std::string(evt.status.ToUTF8())).str()); + auto errormsg = format_wxstr("%1%\n%2%", _L("Error uploading to print host") + ":", evt.status); job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6e4f64a99..3cdd84662 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -164,8 +164,8 @@ void Tab::create_preset_tab() m_btn_compare_preset->SetToolTip(_L("Compare this preset with some another")); // TRN "Save current Settings" - m_btn_save_preset->SetToolTip(from_u8((boost::format(_utf8(L("Save current %s"))) % m_title).str())); - m_btn_rename_preset->SetToolTip(from_u8((boost::format(_utf8(L("Rename current %s"))) % m_title).str())); + m_btn_save_preset->SetToolTip(format_wxstr(_L("Save current %s"), m_title)); + m_btn_rename_preset->SetToolTip(format_wxstr(_L("Rename current %s"), m_title)); m_btn_rename_preset->Hide(); m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); m_btn_delete_preset->Hide(); @@ -3924,7 +3924,7 @@ void Tab::delete_preset() { auto current_preset = m_presets->get_selected_preset(); // Don't let the user delete the ' - default - ' configuration. - std::string action = current_preset.is_external ? _utf8(L("remove")) : _utf8(L("delete")); + wxString action = current_preset.is_external ? _L("remove") : _L("delete"); // TRN remove/delete PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; @@ -3973,9 +3973,9 @@ void Tab::delete_preset() msg += from_u8((boost::format(_u8L("Are you sure you want to %1% the selected preset?")) % action).str()); } - action = current_preset.is_external ? _utf8(L("Remove")) : _utf8(L("Delete")); + action = current_preset.is_external ? _L("Remove") : _L("Delete"); // TRN Remove/Delete - wxString title = from_u8((boost::format(_utf8(L("%1% Preset"))) % action).str()); //action + _(L(" Preset")); + wxString title = format_wxstr(_L("%1% Preset"), action); if (current_preset.is_default || //wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) wxID_YES != MessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) @@ -4061,7 +4061,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep deps.checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All"))); deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font()); wxGetApp().UpdateDarkUI(deps.checkbox, false, true); - deps.btn = new ScalableButton(parent, wxID_ANY, "printer", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), + deps.btn = new ScalableButton(parent, wxID_ANY, "printer", format_wxstr(" %s %s", _L("Set"), dots), wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); deps.btn->SetSize(deps.btn->GetBestSize()); @@ -5024,9 +5024,9 @@ void TabSLAPrint::update_description_lines() { bool elev = !m_config->opt_bool("pad_enable") || !m_config->opt_bool("pad_around_object"); m_support_object_elevation_description_line->SetText(elev ? "" : - from_u8((boost::format(_u8L("\"%1%\" is disabled because \"%2%\" is on in \"%3%\" category.\n" - "To enable \"%1%\", please switch off \"%2%\"")) - % _L("Object elevation") % _L("Pad around object") % _L("Pad")).str())); + format_wxstr(_L("\"%1%\" is disabled because \"%2%\" is on in \"%3%\" category.\n" + "To enable \"%1%\", please switch off \"%2%\"") + , _L("Object elevation"), _L("Pad around object"), _L("Pad"))); } } } diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 2b473ae0c..0d4dc576d 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -436,8 +436,7 @@ MsgDataIncompatible::~MsgDataIncompatible() {} MsgDataLegacy::MsgDataLegacy() : MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update"))) { - auto *text = new wxStaticText(this, wxID_ANY, from_u8((boost::format( - _utf8(L( + auto *text = new wxStaticText(this, wxID_ANY, format_wxstr( _L( "%s now uses an updated configuration structure.\n\n" "So called 'System presets' have been introduced, which hold the built-in default settings for various " @@ -447,10 +446,8 @@ MsgDataLegacy::MsgDataLegacy() : "Please proceed with the %s that follows to set up the new presets " "and to choose whether to enable automatic preset updates." - ))) - % SLIC3R_APP_NAME - % _utf8(ConfigWizard::name())).str() - )); + ) + , SLIC3R_APP_NAME, ConfigWizard::name())); text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); content_sizer->AddSpacer(VERT_SPACING); @@ -458,7 +455,7 @@ MsgDataLegacy::MsgDataLegacy() : auto *text2 = new wxStaticText(this, wxID_ANY, _(L("For more information please visit our wiki page:"))); static const wxString url("https://github.com/prusa3d/PrusaSlicer/wiki/Slic3r-PE-1.40-configuration-update"); // The wiki page name is intentionally not localized: - auto *link = new wxHyperlinkCtrl(this, wxID_ANY, wxString::Format("%s 1.40 configuration update", SLIC3R_APP_NAME), CONFIG_UPDATE_WIKI_URL); + auto *link = new wxHyperlinkCtrl(this, wxID_ANY, format_wxstr(_L("%s 1.40 configuration update"), SLIC3R_APP_NAME), CONFIG_UPDATE_WIKI_URL); content_sizer->Add(text2); content_sizer->Add(link); content_sizer->AddSpacer(VERT_SPACING); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index d4bd27c05..f67ad287a 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -630,9 +630,8 @@ ModeButton::ModeButton( wxWindow* parent, void ModeButton::Init(const wxString &mode) { - std::string mode_str = std::string(mode.ToUTF8()); - m_tt_focused = Slic3r::GUI::from_u8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str()); - m_tt_selected = Slic3r::GUI::from_u8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str()); + m_tt_focused = Slic3r::GUI::format_wxstr(_L("Switch to the %s mode"), mode); + m_tt_selected = Slic3r::GUI::format_wxstr(_L("Current mode is %s"), mode); SetBitmapMargins(3, 0); diff --git a/src/slic3r/Utils/AppUpdater.cpp b/src/slic3r/Utils/AppUpdater.cpp index 9eb012827..4c60f018e 100644 --- a/src/slic3r/Utils/AppUpdater.cpp +++ b/src/slic3r/Utils/AppUpdater.cpp @@ -39,7 +39,7 @@ namespace { std::string msg; bool res = GUI::create_process(path, std::wstring(), msg); if (!res) { - std::string full_message = GUI::format(_utf8("Running downloaded instaler of %1% has failed:\n%2%"), SLIC3R_APP_NAME, msg); + std::string full_message = GUI::format(_u8L("Running downloaded instaler of %1% has failed:\n%2%"), SLIC3R_APP_NAME, msg); BOOST_LOG_TRIVIAL(error) << full_message; // lm: maybe UI error msg? // dk: bellow. (maybe some general show error evt would be better?) wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); evt->SetString(full_message); @@ -174,9 +174,7 @@ bool AppUpdater::priv::http_get_file(const std::string& url, size_t size_limit, cancel = (m_cancel ? true : !progress_fn(std::move(progress))); if (cancel) { // Lets keep error_message empty here - if there is need to show error dialog, the message will be probably shown by whatever caused the cancel. - /* - error_message = GUI::format(_utf8("Error getting: `%1%`: Download was canceled."), url); - */ + //error_message = GUI::format(_u8L("Error getting: `%1%`: Download was canceled."), url); BOOST_LOG_TRIVIAL(debug) << "AppUpdater::priv::http_get_file message: "<< error_message; } }) @@ -205,8 +203,8 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d assert(!dest_path.empty()); if (dest_path.empty()) { - std::string line1 = GUI::format(_utf8("Internal download error for url %1%:"), data.url); - std::string line2 = _utf8("Destination path is empty."); + std::string line1 = GUI::format(_u8L("Internal download error for url %1%:"), data.url); + std::string line2 = _u8L("Destination path is empty."); std::string message = GUI::format("%1%\n%2%", line1, line2); BOOST_LOG_TRIVIAL(error) << message; wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); @@ -222,8 +220,8 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d file = fopen(temp_path_wstring.c_str(), "wb"); assert(file != NULL); if (file == NULL) { - std::string line1 = GUI::format(_utf8("Download from %1% couldn't start:"), data.url); - std::string line2 = GUI::format(_utf8("Can't create file at %1%."), tmp_path.string()); + std::string line1 = GUI::format(_u8L("Download from %1% couldn't start:"), data.url); + std::string line2 = GUI::format(_u8L("Can't create file at %1%."), tmp_path.string()); std::string message = GUI::format("%1%\n%2%", line1, line2); BOOST_LOG_TRIVIAL(error) << message; wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); @@ -264,11 +262,11 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d // Size check. Does always 1 char == 1 byte? size_t body_size = body.size(); if (body_size != expected_size) { - error_message = GUI::format(_utf8("Downloaded file has wrong size. Expected size: %1% Downloaded size: %2%"), expected_size, body_size); + error_message = GUI::format(_u8L("Downloaded file has wrong size. Expected size: %1% Downloaded size: %2%"), expected_size, body_size); return false; } if (file == NULL) { - error_message = GUI::format(_utf8("Can't create file at %1%."), tmp_path.string()); + error_message = GUI::format(_u8L("Can't create file at %1%."), tmp_path.string()); return false; } try @@ -279,7 +277,7 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d } catch (const std::exception& e) { - error_message = GUI::format(_utf8("Failed to write to file or to move %1% to %2%:\n%3%"), tmp_path, dest_path, e.what()); + error_message = GUI::format(_u8L("Failed to write to file or to move %1% to %2%:\n%3%"), tmp_path, dest_path, e.what()); return false; } return true; @@ -295,7 +293,7 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d } else { std::string message = (error_message.empty() ? std::string() - : GUI::format(_utf8("Downloading new %1% has failed:\n%2%"), SLIC3R_APP_NAME, error_message)); + : GUI::format(_u8L("Downloading new %1% has failed:\n%2%"), SLIC3R_APP_NAME, error_message)); wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); if (!message.empty()) { BOOST_LOG_TRIVIAL(error) << message; diff --git a/src/slic3r/Utils/AstroBox.cpp b/src/slic3r/Utils/AstroBox.cpp index a2b5bca04..e7044ad20 100644 --- a/src/slic3r/Utils/AstroBox.cpp +++ b/src/slic3r/Utils/AstroBox.cpp @@ -66,7 +66,7 @@ bool AstroBox::test(wxString &msg) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (! res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "AstroBox")).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "AstroBox")); } } catch (const std::exception &) { @@ -86,10 +86,10 @@ wxString AstroBox::get_test_ok_msg () const wxString AstroBox::get_test_failed_msg (wxString &msg) const { - return GUI::from_u8((boost::format("%s: %s\n\n%s") - % _utf8(L("Could not connect to AstroBox")) - % std::string(msg.ToUTF8()) - % _utf8(L("Note: AstroBox version at least 1.1.0 is required."))).str()); + return GUI::format_wxstr("%s: %s\n\n%s" + , _L("Could not connect to AstroBox") + , msg + , _L("Note: AstroBox version at least 1.1.0 is required.")); } bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 229d0c950..74bf080cd 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -49,9 +49,7 @@ wxString Duet::get_test_ok_msg () const wxString Duet::get_test_failed_msg (wxString &msg) const { - return GUI::from_u8((boost::format("%s: %s") - % _utf8(L("Could not connect to Duet")) - % std::string(msg.ToUTF8())).str()); + return GUI::format_wxstr("%s: %s", _L("Could not connect to Duet"), msg); } bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const diff --git a/src/slic3r/Utils/FlashAir.cpp b/src/slic3r/Utils/FlashAir.cpp index e54dca58f..157d9623f 100644 --- a/src/slic3r/Utils/FlashAir.cpp +++ b/src/slic3r/Utils/FlashAir.cpp @@ -70,10 +70,10 @@ wxString FlashAir::get_test_ok_msg () const wxString FlashAir::get_test_failed_msg (wxString &msg) const { - return GUI::from_u8((boost::format("%s: %s\n%s") - % _utf8(L("Could not connect to FlashAir")) - % std::string(msg.ToUTF8()) - % _utf8(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))).str()); + return GUI::format_wxstr("%s: %s\n%s" + , _u8L("Could not connect to FlashAir") + , msg + , _u8L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required.")); } bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 109283fc6..cce212631 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -57,9 +57,7 @@ wxString MKS::get_test_ok_msg() const wxString MKS::get_test_failed_msg(wxString& msg) const { - return GUI::from_u8((boost::format("%s: %s") - % _utf8(L("Could not connect to MKS")) - % std::string(msg.ToUTF8())).str()); + return GUI::format_wxstr("%s: %s", _L("Could not connect to MKS"), msg); } bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 413a3445a..d8d977c05 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -203,7 +203,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (!res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint")); } } catch (const std::exception&) { @@ -252,7 +252,7 @@ bool OctoPrint::test(wxString& msg) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (! res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint")); } } catch (const std::exception &) { @@ -280,10 +280,10 @@ wxString OctoPrint::get_test_ok_msg () const wxString OctoPrint::get_test_failed_msg (wxString &msg) const { - return GUI::from_u8((boost::format("%s: %s\n\n%s") - % _utf8(L("Could not connect to OctoPrint")) - % std::string(msg.ToUTF8()) - % _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str()); + return GUI::format_wxstr("%s: %s\n\n%s" + , _L("Could not connect to OctoPrint") + , msg + , _L("Note: OctoPrint version at least 1.1.0 is required.")); } bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const @@ -530,9 +530,7 @@ wxString SL1Host::get_test_ok_msg () const wxString SL1Host::get_test_failed_msg (wxString &msg) const { - return GUI::from_u8((boost::format("%s: %s") - % _utf8(L("Could not connect to Prusa SLA")) - % std::string(msg.ToUTF8())).str()); + return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa SLA"), msg); } bool SL1Host::validate_version_text(const boost::optional &version_text) const @@ -575,9 +573,7 @@ wxString PrusaLink::get_test_ok_msg() const wxString PrusaLink::get_test_failed_msg(wxString& msg) const { - return GUI::from_u8((boost::format("%s: %s") - % _utf8(L("Could not connect to PrusaLink")) - % std::string(msg.ToUTF8())).str()); + return GUI::format_wxstr("%s: %s", _L("Could not connect to PrusaLink"), msg); } bool PrusaLink::validate_version_text(const boost::optional& version_text) const @@ -663,7 +659,7 @@ bool PrusaLink::test(wxString& msg) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (!res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint")); } } catch (const std::exception&) { @@ -821,7 +817,7 @@ bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (!res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint")); use_put = false; return; } @@ -894,7 +890,7 @@ bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_ const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (!res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint")); use_put = false; return; } diff --git a/src/slic3r/Utils/Repetier.cpp b/src/slic3r/Utils/Repetier.cpp index e266be1f8..274ea4351 100644 --- a/src/slic3r/Utils/Repetier.cpp +++ b/src/slic3r/Utils/Repetier.cpp @@ -85,7 +85,7 @@ bool Repetier::test(wxString &msg) const const auto soft = ptree.get_optional("software"); res = validate_repetier(text, soft); if (! res) { - msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (soft ? *soft : (text ? *text : "Repetier"))).str()); + msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (soft ? *soft : (text ? *text : "Repetier"))); } } catch (const std::exception &) { @@ -105,10 +105,10 @@ wxString Repetier::get_test_ok_msg () const wxString Repetier::get_test_failed_msg (wxString &msg) const { - return GUI::from_u8((boost::format("%s: %s\n\n%s") - % _utf8(L("Could not connect to Repetier")) - % std::string(msg.ToUTF8()) - % _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str()); + return GUI::format_wxstr("%s: %s\n\n%s" + , _L("Could not connect to Repetier") + , msg + , _L("Note: Repetier version at least 0.90.0 is required.")); } bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const @@ -142,7 +142,7 @@ bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error auto http = Http::post(std::move(url)); set_auth(http); - if (! upload_data.group.empty() && upload_data.group != _utf8(L("Default"))) { + if (! upload_data.group.empty() && upload_data.group != _u8L("Default")) { http.form_add("group", upload_data.group); } @@ -223,7 +223,7 @@ bool Repetier::get_groups(wxArrayString& groups) const BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("groupNames.")) { if (v.second.data() == "#") { - groups.push_back(_utf8(L("Default"))); + groups.push_back(_L("Default")); } else { // Is it safe to assume that the data are utf-8 encoded? groups.push_back(GUI::from_u8(v.second.data())); From 18e56c3d532d02662bb8e399cd0fd40578031a0a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 20 Mar 2023 10:40:42 +0100 Subject: [PATCH 143/201] Localization: Fixes for some phrases + Added/Fixed comments where it's needed --- src/libslic3r/I18N.hpp | 1 + src/libslic3r/PrintConfig.cpp | 42 ++++++++++++++------ src/libslic3r/PrintObject.cpp | 3 +- src/libslic3r/SLAPrintSteps.cpp | 1 + src/slic3r/GUI/AboutDialog.cpp | 3 +- src/slic3r/GUI/ButtonsDescription.cpp | 2 +- src/slic3r/GUI/ConfigWizard.cpp | 25 +++++++----- src/slic3r/GUI/Downloader.cpp | 2 +- src/slic3r/GUI/DownloaderFileGet.cpp | 1 + src/slic3r/GUI/GUI_App.cpp | 4 +- src/slic3r/GUI/GUI_ObjectList.cpp | 7 ++-- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 +-- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 +++-- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 39 +++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 4 +- src/slic3r/GUI/Jobs/EmbossJob.cpp | 18 ++++----- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 8 ++-- src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/OG_CustomCtrl.cpp | 10 ++--- src/slic3r/GUI/OptionsGroup.cpp | 4 +- src/slic3r/GUI/Plater.cpp | 14 +++---- src/slic3r/GUI/Preferences.cpp | 9 +---- src/slic3r/GUI/PrintHostDialogs.cpp | 4 +- src/slic3r/GUI/SavePresetDialog.cpp | 9 ++++- src/slic3r/GUI/SurfaceDrag.cpp | 1 + src/slic3r/GUI/Tab.cpp | 11 ++--- src/slic3r/GUI/UpdateDialogs.cpp | 8 ++-- src/slic3r/Utils/OctoPrint.cpp | 15 ++++--- 32 files changed, 149 insertions(+), 124 deletions(-) diff --git a/src/libslic3r/I18N.hpp b/src/libslic3r/I18N.hpp index ee39df2e1..3cc196b6a 100644 --- a/src/libslic3r/I18N.hpp +++ b/src/libslic3r/I18N.hpp @@ -29,6 +29,7 @@ namespace I18N { #endif namespace { const char* L(const char* s) { return s; } + const char* L_CONTEXT(const char* s, const char* context) { return s; } std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } } #endif diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d52f38866..22f296456 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -399,6 +399,7 @@ void PrintConfigDef::init_fff_params() const int max_temp = 1500; def = this->add("avoid_crossing_curled_overhangs", coBool); def->label = L("Avoid crossing curled overhangs (Experimental)"); + // TRN PrintSettings: "Avoid crossing curled overhangs (Experimental)" def->tooltip = L("Plan travel moves such that the extruder avoids areas where the filament may be curled up. " "This is mostly happening on steeper rounded overhangs and may cause a crash with the nozzle. " "This feature slows down both the print and the G-code generation."); @@ -456,8 +457,8 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionString("")); def = this->add("bottom_solid_layers", coInt); - //TRN To be shown in Print Settings "Bottom solid layers" - def->label = L("Bottom"); + //TRN Print Settings: "Bottom solid layers" + def->label = L_CONTEXT("Bottom", "Layers"); def->category = L("Layers and Perimeters"); def->tooltip = L("Number of solid layers to generate on bottom surfaces."); def->full_label = L("Bottom solid layers"); @@ -465,8 +466,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(3)); def = this->add("bottom_solid_min_thickness", coFloat); - //TRN To be shown in Print Settings "Top solid layers" - def->label = L("Bottom"); + def->label = L_CONTEXT("Bottom", "Layers"); def->category = L("Layers and Perimeters"); def->tooltip = L("The number of bottom solid layers is increased above bottom_solid_layers if necessary to satisfy " "minimum thickness of bottom shell."); @@ -533,6 +533,7 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); + // TRN PrintSettings : "Dynamic overhang speed" auto overhang_speed_setting_description = L("Overhang size is expressed as a percentage of overlap of the extrusion with the previous layer: " "100% would be full overlap (no overhang), while 0% represents full overhang (floating extrusion, bridge). " "Speeds for overhang sizes in between are calculated via linear interpolation. " @@ -580,10 +581,11 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBools{false}); + // TRN FilamentSettings : "Dynamic fan speeds" auto fan_speed_setting_description = L( "Overhang size is expressed as a percentage of overlap of the extrusion with the previous layer: " "100% would be full overlap (no overhang), while 0% represents full overhang (floating extrusion, bridge). " - "Fan speeds for overhang sizes in between are calculated via linear interpolation. "); + "Fan speeds for overhang sizes in between are calculated via linear interpolation."); def = this->add("overhang_fan_speed_0", coInts); def->label = L("speed for 0% overlap (bridge)"); @@ -1961,7 +1963,8 @@ void PrintConfigDef::init_fff_params() def = this->add("ooze_prevention", coBool); def->label = L("Enable"); - def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing. "); + // TRN PrintSettings: Enable ooze prevention + def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing."); def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); @@ -2299,6 +2302,7 @@ void PrintConfigDef::init_fff_params() def = this->add("staggered_inner_seams", coBool); def->label = L("Staggered inner seams"); + // TRN PrintSettings: "Staggered inner seams" def->tooltip = L("This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); @@ -2464,6 +2468,7 @@ void PrintConfigDef::init_fff_params() def = this->add("standby_temperature_delta", coInt); def->label = L("Temperature variation"); + // TRN PrintSettings : "Ooze prevention" > "Temperature variation" def->tooltip = L("Temperature difference to be applied when an extruder is not active. " "The value is not used when 'idle_temperature' in filament settings " "is defined."); @@ -2641,8 +2646,8 @@ void PrintConfigDef::init_fff_params() "If set to zero, support_material_contact_distance will be used for both top and bottom contact Z distances."); def->sidetext = L("mm"); // def->min = 0; - //TRN To be shown in Print Settings "Bottom contact Z distance". Have to be as short as possible def->set_enum_values(ConfigOptionDef::GUIType::f_enum_open, { + //TRN Print Settings: "Bottom contact Z distance". Have to be as short as possible { "0", L("Same as top") }, { "0.1", "0.1" }, { "0.2", "0.2" } @@ -2722,8 +2727,8 @@ void PrintConfigDef::init_fff_params() "Set to -1 to use support_material_interface_layers"); def->sidetext = L("layers"); def->min = -1; - //TRN To be shown in Print Settings "Bottom interface layers". Have to be as short as possible def->set_enum_values(ConfigOptionDef::GUIType::i_enum_open, { + //TRN Print Settings: "Bottom interface layers". Have to be as short as possible { "-1", L("Same as top") }, { "0", L("0 (off)") }, { "1", L("1 (light)") }, @@ -2824,6 +2829,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_material_synchronize_layers", coBool); def->label = L("Synchronize with object layers"); def->category = L("Support material"); + // TRN PrintSettings : "Synchronize with object layers" def->tooltip = L("Synchronize support layers with the object print layers. This is useful " "with multi-material printers, where the extruder switch is expensive. " "This option is only available when top contact Z distance is set to zero."); @@ -2855,6 +2861,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_tree_angle", coFloat); def->label = L("Maximum Branch Angle"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Maximum Branch Angle" def->tooltip = L("The maximum angle of the branches, when the branches have to avoid the model. " "Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."); def->sidetext = L("°"); @@ -2866,6 +2873,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_tree_angle_slow", coFloat); def->label = L("Preferred Branch Angle"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Preferred Branch Angle" def->tooltip = L("The preferred angle of the branches, when they do not have to avoid the model. " "Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster."); def->sidetext = L("°"); @@ -2877,6 +2885,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_tree_tip_diameter", coFloat); def->label = L("Tip Diameter"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Tip Diameter" def->tooltip = L("The diameter of the top of the tip of the branches of organic support."); def->sidetext = L("mm"); def->min = 0; @@ -2886,6 +2895,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_tree_branch_diameter", coFloat); def->label = L("Branch Diameter"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Branch Diameter" def->tooltip = L("The diameter of the thinnest branches of organic support. Thicker branches are more sturdy. " "Branches towards the base will be thicker than this."); def->sidetext = L("mm"); @@ -2894,8 +2904,10 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(2)); def = this->add("support_tree_branch_diameter_angle", coFloat); + // TRN PrintSettings: #lmFIXME def->label = L("Branch Diameter Angle"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Branch Diameter Angle" def->tooltip = L("The angle of the branches' diameter as they gradually become thicker towards the bottom. " "An angle of 0 will cause the branches to have uniform thickness over their length. " "A bit of an angle can increase stability of the organic support."); @@ -2909,8 +2921,10 @@ void PrintConfigDef::init_fff_params() // How far apart the branches need to be when they touch the model. Making this distance small will cause // the tree support to touch the model at more points, causing better overhang but making support harder to remove. def = this->add("support_tree_branch_distance", coFloat); + // TRN PrintSettings: #lmFIXME def->label = L("Branch Distance"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Branch Distance" def->tooltip = L("How far apart the branches need to be when they touch the model. " "Making this distance small will cause the tree support to touch the model at more points, " "causing better overhang but making support harder to remove."); @@ -2920,6 +2934,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_tree_top_rate", coPercent); def->label = L("Branch Density"); def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Branch Density" def->tooltip = L("Adjusts the density of the support structure used to generate the tips of the branches. " "A higher value results in better overhangs but the supports are harder to remove, " "thus it is recommended to enable top support interfaces instead of a high branch density value " @@ -3008,8 +3023,8 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloatOrPercent(15, false)); def = this->add("top_solid_layers", coInt); - //TRN To be shown in Print Settings "Top solid layers" - def->label = L("Top"); + //TRN Print Settings: "Top solid layers" + def->label = L_CONTEXT("Top", "Layers"); def->category = L("Layers and Perimeters"); def->tooltip = L("Number of solid layers to generate on top surfaces."); def->full_label = L("Top solid layers"); @@ -3017,8 +3032,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(3)); def = this->add("top_solid_min_thickness", coFloat); - //TRN To be shown in Print Settings "Top solid layers" - def->label = L("Top"); + def->label = L_CONTEXT("Top", "Layers"); def->category = L("Layers and Perimeters"); def->tooltip = L("The number of top solid layers is increased above top_solid_layers if necessary to satisfy " "minimum thickness of top shell." @@ -3874,7 +3888,9 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Support tree building strategy"); def->set_enum( ConfigOptionEnum::get_enum_names(), - { L("Default"), L("Branching (experimental)") }); + { L("Default"), + // TRN One of the "Support tree type"s on SLAPrintSettings : Supports + L("Branching (experimental)") }); // TODO: def->enum_def->labels[2] = L("Organic"); def->mode = comSimple; def->set_default_value(new ConfigOptionEnum(sla::SupportTreeType::Default)); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index eefedd143..7136617d8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -399,7 +399,8 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { - m_print->set_status(45, _u8L("making infill")); + // TRN Status for the Print calculation + m_print->set_status(45, _u8L("Making infill")); const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 94ddcec72..5ef8a2709 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -51,6 +51,7 @@ const std::array OBJ_STEP_LEVELS = { std::string OBJ_STEP_LABELS(size_t idx) { switch (idx) { + // TRN Status of the SLA print calculation case slaposAssembly: return _u8L("Assembling model from parts"); case slaposHollowing: return _u8L("Hollowing model"); case slaposDrillHoles: return _u8L("Drilling holes into model."); diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 132159ff5..0d59f4433 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -141,7 +141,6 @@ wxString CopyrightsDialog::get_html_text() const auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue())); const wxString copyright_str = _L("Copyright") + "© "; - // TRN "Slic3r _is licensed under the_ License" const wxString header_str = _L("License agreements of all following programs (libraries) are part of application license agreement"); wxString text = wxString::Format( @@ -268,7 +267,7 @@ AboutDialog::AboutDialog() m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); m_html->SetBorders(2); const wxString copyright_str = _L("Copyright"); - // TRN "Slic3r _is licensed under the_ License" + // TRN AboutDialog: "Slic3r %1% GNU Affero General Public License" const wxString is_lecensed_str = _L("is licensed under the"); const wxString license_str = _L("GNU Affero General Public License, version 3"); const wxString based_on_str = _L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community."); diff --git a/src/slic3r/GUI/ButtonsDescription.cpp b/src/slic3r/GUI/ButtonsDescription.cpp index e225c7db7..e6fd519df 100644 --- a/src/slic3r/GUI/ButtonsDescription.cpp +++ b/src/slic3r/GUI/ButtonsDescription.cpp @@ -160,7 +160,7 @@ void FillSizerWithModeColorDescriptions( wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(9, 5, 5); sizer->Add(grid_sizer, 0, wxEXPAND); - const std::vector names = { _L("Simple"), _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), _L("Expert") }; + const std::vector names = { _L("Simple"), _CTX("Advanced", "Mode"), _L("Expert") }; for (size_t mode = 0; mode < names.size(); ++mode) { wxColour& color = mode_palette[mode]; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index aa2de94af..4aa8c9df6 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -787,11 +787,14 @@ void PageMaterials::set_compatible_printers_html_window(const std::vectortechnology == T_FFF && template_shown) { + // TRN ConfigWizard: Materials : "%1%" = "Filaments"/"SLA materials" text = format_wxstr(_L("%1% visible for (\"Template\") printer are universal profiles available for all printers. These might not be compatible with your printer."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials")); } else { + // TRN ConfigWizard: Materials : "%1%" = "Filaments"/"SLA materials" wxString first_line = format_wxstr(_L("%1% marked with * are not compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials")); if (all_printers) { + // TRN ConfigWizard: Materials : "%1%" = "filament"/"SLA material" wxString second_line = format_wxstr(_L("All installed printers are compatible with the selected %1%."), materials->technology == T_FFF ? _L("filament") : _L("SLA material")); text = wxString::Format( "" @@ -1369,7 +1372,7 @@ Worker::Worker(wxWindow* parent) button_path->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { boost::filesystem::path chosen_dest(boost::nowide::narrow(m_input_path->GetValue())); - wxDirDialog dialog(m_parent, L("Choose folder:"), chosen_dest.string() ); + wxDirDialog dialog(m_parent, _L("Choose folder") + ":", chosen_dest.string() ); if (dialog.ShowModal() == wxID_OK) this->m_input_path->SetValue(dialog.GetPath()); }); @@ -1421,11 +1424,12 @@ PageDownloader::PageDownloader(ConfigWizard* parent) box_allow_downloads->SetValue(box_allow_value); append(box_allow_downloads); - append_text(wxString::Format(_L( - "If enabled, %s registers to start on custom URL on www.printables.com." - " You will be able to use button with %s logo to open models in this %s." + // TRN ConfigWizard : Downloader : %1% = "PrusaSlicer" + append_text(format_wxstr(_L( + "If enabled, %1% registers to start on custom URL on www.printables.com." + " You will be able to use button with %1% logo to open models in this %1%." " The model will be downloaded into folder you choose bellow." - ), SLIC3R_APP_NAME, SLIC3R_APP_NAME, SLIC3R_APP_NAME)); + ), SLIC3R_APP_NAME)); #ifdef __linux__ append_text(wxString::Format(_L( @@ -1456,7 +1460,7 @@ bool DownloaderUtils::Worker::perform_register(const std::string& path_override/ chosen_dest = aux_dest; ec.clear(); if (chosen_dest.empty() || !boost::filesystem::is_directory(chosen_dest, ec) || ec) { - std::string err_msg = GUI::format("%1%\n\n%2%",_L("Chosen directory for downloads does not Exists.") ,chosen_dest.string()); + std::string err_msg = GUI::format("%1%\n\n%2%",_L("Chosen directory for downloads does not exist.") ,chosen_dest.string()); BOOST_LOG_TRIVIAL(error) << err_msg; show_error(m_parent, err_msg); return false; @@ -1753,6 +1757,7 @@ void PageBedShape::apply_custom_config(DynamicPrintConfig &config) } PageBuildVolume::PageBuildVolume(ConfigWizard* parent) + // TRN ConfigWizard : Size of possible print, related on printer size : ConfigWizardPage(parent, _L("Build Volume"), _L("Build Volume"), 1) , build_volume(new DiamTextCtrl(this)) { @@ -1793,7 +1798,7 @@ PageBuildVolume::PageBuildVolume(ConfigWizard* parent) }, build_volume->GetId()); auto* sizer_volume = new wxFlexGridSizer(3, 5, 5); - auto* text_volume = new wxStaticText(this, wxID_ANY, _L("Max print height:")); + auto* text_volume = new wxStaticText(this, wxID_ANY, _L("Max print height") + ":"); auto* unit_volume = new wxStaticText(this, wxID_ANY, _L("mm")); sizer_volume->AddGrowableCol(0, 1); sizer_volume->Add(text_volume, 0, wxALIGN_CENTRE_VERTICAL); @@ -1829,7 +1834,7 @@ PageDiameters::PageDiameters(ConfigWizard *parent) append_text(_L("Enter the diameter of your printer's hot end nozzle.")); auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5); - auto *text_nozzle = new wxStaticText(this, wxID_ANY, _L("Nozzle Diameter:")); + auto *text_nozzle = new wxStaticText(this, wxID_ANY, _L("Nozzle Diameter") + ":"); auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm")); sizer_nozzle->AddGrowableCol(0, 1); sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); @@ -1843,7 +1848,7 @@ PageDiameters::PageDiameters(ConfigWizard *parent) append_text(_L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.")); auto *sizer_filam = new wxFlexGridSizer(3, 5, 5); - auto *text_filam = new wxStaticText(this, wxID_ANY, _L("Filament Diameter:")); + auto *text_filam = new wxStaticText(this, wxID_ANY, _L("Filament Diameter") + ":"); auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm")); sizer_filam->AddGrowableCol(0, 1); sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); @@ -1935,7 +1940,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent) append_text(_L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.")); auto *sizer_bed = new wxFlexGridSizer(3, 5, 5); - auto *text_bed = new wxStaticText(this, wxID_ANY, _L("Bed Temperature:")); + auto *text_bed = new wxStaticText(this, wxID_ANY, _L("Bed Temperature") + ":"); auto *unit_bed = new wxStaticText(this, wxID_ANY, _L("°C")); sizer_bed->AddGrowableCol(0, 1); sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL); diff --git a/src/slic3r/GUI/Downloader.cpp b/src/slic3r/GUI/Downloader.cpp index 45ea43631..3d2a00106 100644 --- a/src/slic3r/GUI/Downloader.cpp +++ b/src/slic3r/GUI/Downloader.cpp @@ -178,7 +178,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())); + show_error(nullptr, format_wxstr(L"%1%\n%2%", _L("The download has failed") + ":", event.GetString())); } void Downloader::on_complete(wxCommandEvent& event) { diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index ee407afdd..ee6455259 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -190,6 +190,7 @@ void FileGet::priv::get_perform() //assert(file != NULL); if (file == NULL) { wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR); + // TRN %1% = file path evt->SetString(GUI::format_wxstr(_L("Can't create file at %1%."), temp_path_wstring)); evt->SetInt(m_id); m_evt_handler->QueueEvent(evt); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index fe1fd2012..1d4a53fd9 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1988,7 +1988,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const void GUI_App::import_zip(wxWindow* parent, wxString& input_file) const { wxFileDialog dialog(parent ? parent : GetTopWindow(), - _L("Choose ZIP file:"), + _L("Choose ZIP file") + ":", from_u8(app_config->get_last_dir()), "", file_wildcards(FT_ZIP), wxFD_OPEN | wxFD_FILE_MUST_EXIST); @@ -2416,7 +2416,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) mode_menu = new wxMenu(); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _L("Simple"), _L("Simple View Mode")); // mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _L("Advanced"), _L("Advanced View Mode")); - mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), _L("Advanced View Mode")); + mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _CTX("Advanced", "Mode"), _L("Advanced View Mode")); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _L("Expert"), _L("Expert View Mode")); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index ae0e54b45..ad2758f9e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -420,7 +420,7 @@ MeshErrorsInfo ObjectList::get_mesh_errors_info(const int obj_idx, const int vol const ModelObject* object = (*m_objects)[obj_idx]; if (vol_idx != -1 && vol_idx >= int(object->volumes.size())) { if (sidebar_info) - *sidebar_info = _L("Wrong volume index "); + *sidebar_info = _L("Wrong volume index") + " "; return { {}, {} }; // hide tooltip } @@ -2063,9 +2063,8 @@ bool ObjectList::del_from_cut_object(bool is_cut_connector, bool is_model_part/* InfoDialog dialog(wxGetApp().plater(), title, _L("This action will break a cut information.\n" - "After that PrusaSlicer can't guarantee model consistency.\n" - "\n" - "To manipulate with solid parts or negative volumes you have to invalidate cut infornation first." + msg_end ), + "After that PrusaSlicer can't guarantee model consistency.") + "\n\n" + + _L("To manipulate with solid parts or negative volumes you have to invalidate cut infornation first." + msg_end ), false, buttons_style | wxCANCEL_DEFAULT | wxICON_WARNING); dialog.SetButtonLabel(wxID_YES, _L("Invalidate cut info")); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 415f008fb..7d9a87b58 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -272,11 +272,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // We will add a button to toggle mirroring to each axis: auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); #if ENABLE_WORLD_COORDINATE - btn->SetToolTip(_L("Mirror along") + wxString::Format(_L(" %c "), (int)label) + _L("axis")); - + btn->SetToolTip(format_wxstr(_L("Mirror along %1% axis"), label)); m_mirror_buttons[axis_idx] = btn; #else - btn->SetToolTip(wxString::Format(_L("Toggle %c axis mirroring"), (int)label)); + btn->SetToolTip(format_wxstr(_L("Toggle %1% axis mirroring"), label)); btn->SetBitmapDisabled_(m_mirror_bitmap_hidden); m_mirror_buttons[axis_idx].first = btn; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e4a7e9613..410cf6753 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -182,9 +182,9 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_style (size_t(CutConnectorStyle::Prism)) , m_connector_shape_id (size_t(CutConnectorShape::Circle)) { - m_modes = { _u8L("Planar")//, _u8L("Grid") +// m_modes = { _u8L("Planar"), _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") - }; +// }; m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; @@ -231,7 +231,7 @@ std::string GLGizmoCut3D::get_tooltip() const std::string tooltip; if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); + std::string unit_str = " " + (m_imperial_units ? _u8L("in") : _u8L("mm")); const BoundingBoxf3& tbb = m_transformed_bounding_box; const std::string name = m_keep_as_parts ? _u8L("Part") : _u8L("Object"); @@ -1671,7 +1671,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) render_build_size(); ImGui::AlignTextToFramePadding(); - ImGuiWrapper::text(_L("Cut position: ")); + ImGuiWrapper::text(_L("Cut position") + ": "); ImGui::SameLine(); render_move_center_input(Z); ImGui::SameLine(); @@ -1778,9 +1778,11 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGuiWrapper::text(_L("Cut to") + ":"); add_horizontal_scaled_interval(1.2f); + // TRN CutGizmo: RadioButton Cut to ... if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts)) m_keep_as_parts = false; ImGui::SameLine(); + // TRN CutGizmo: RadioButton Cut to ... if (m_imgui->radio_button(_L("Parts"), m_keep_as_parts)) m_keep_as_parts = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 02ed8d5b6..ee804686e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -151,7 +151,7 @@ class GLGizmoCut3D : public GLGizmoBase , Manual }; - std::vector m_modes; +// std::vector m_modes; size_t m_mode{ size_t(CutMode::cutPlanar) }; std::vector m_connector_modes; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index dd18cb878..8c4b4ff70 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1329,6 +1329,7 @@ void GLGizmoEmboss::draw_text_input() warning_tool_tip += "\n"; warning_tool_tip += t; }; + if (priv::is_text_empty(m_text)) append_warning(_u8L("Embossed text can NOT contain only white spaces.")); if (m_text_contain_unknown_glyph) @@ -1650,9 +1651,9 @@ void GLGizmoEmboss::draw_font_preview(FaceName& face, bool is_visible) // Not finished preview if (is_visible) { // when not canceled still loading - state_text = (face.cancel->load())? - _u8L(" No symbol"): - _u8L(" ... Loading"); + state_text = std::string(" ") + (face.cancel->load() ? + _u8L("No symbol") : + (dots.ToStdString() + _u8L("Loading"))); } else { // not finished and not visible cancel job face.is_created = nullptr; @@ -1702,7 +1703,7 @@ void GLGizmoEmboss::draw_font_preview(FaceName& face, bool is_visible) queue_job(worker, std::move(job)); } else { // cant start new thread at this moment so wait in queue - state_text = _u8L(" ... In queue"); + state_text = " " + dots.ToStdString() + " " + _u8L("Queue"); } if (!state_text.empty()) { @@ -1936,7 +1937,7 @@ void GLGizmoEmboss::draw_font_list() process(); } } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("add file with font(.ttf, .ttc)").c_str()); + ImGui::SetTooltip("Add file with font(.ttf, .ttc)"); #endif // ALLOW_ADD_FONT_BY_FILE #ifdef ALLOW_ADD_FONT_BY_OS_SELECTOR @@ -1946,7 +1947,7 @@ void GLGizmoEmboss::draw_font_list() process(); } } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Open dialog for choose from fonts.").c_str()); + ImGui::SetTooltip("Open dialog for choose from fonts."); #endif // ALLOW_ADD_FONT_BY_OS_SELECTOR } @@ -2031,7 +2032,7 @@ void GLGizmoEmboss::draw_model_type() void GLGizmoEmboss::draw_style_rename_popup() { std::string& new_name = m_style_manager.get_style().name; const std::string &old_name = m_style_manager.get_stored_style()->name; - std::string text_in_popup = GUI::format(_L("Rename style(%1%) for embossing text: "), old_name); + std::string text_in_popup = GUI::format(_L("Rename style(%1%) for embossing text"), old_name) + ": "; ImGui::Text("%s", text_in_popup.c_str()); bool is_unique = true; @@ -2054,9 +2055,9 @@ void GLGizmoEmboss::draw_style_rename_popup() { bool store = false; ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue; if (ImGui::InputText("##rename style", &new_name, flags) && allow_change) store = true; - if (m_imgui->button(_L("ok"), ImVec2(0.f, 0.f), allow_change)) store = true; + if (m_imgui->button(_L("OK"), ImVec2(0.f, 0.f), allow_change)) store = true; ImGui::SameLine(); - if (ImGui::Button(_u8L("cancel").c_str())) { + if (ImGui::Button(_u8L("Cancel").c_str())) { new_name = old_name; ImGui::CloseCurrentPopup(); } @@ -2117,7 +2118,7 @@ void GLGizmoEmboss::draw_style_save_button(bool is_modified) } void GLGizmoEmboss::draw_style_save_as_popup() { - ImGui::Text("%s", _u8L("New name of style: ").c_str()); + ImGui::Text("%s", (_u8L("New name of style") +": ").c_str()); // use name inside of volume configuration as temporary new name std::string &new_name = m_volume->text_configuration->style.name; @@ -2141,11 +2142,11 @@ void GLGizmoEmboss::draw_style_save_as_popup() { if (ImGui::InputText("##save as style", &new_name, flags)) save_style = true; - if (m_imgui->button(_L("ok"), ImVec2(0.f, 0.f), allow_change)) + if (m_imgui->button(_L("OK"), ImVec2(0.f, 0.f), allow_change)) save_style = true; ImGui::SameLine(); - if (ImGui::Button(_u8L("cancel").c_str())){ + if (ImGui::Button(_u8L("Cancel").c_str())){ // write original name to volume TextConfiguration new_name = m_style_manager.get_style().name; ImGui::CloseCurrentPopup(); @@ -2388,7 +2389,7 @@ void GLGizmoEmboss::draw_style_list() { process(); } else { wxString title = _L("Not valid style."); - wxString message = GUI::format_wxstr(_L("Style '%1%' can't be used and will be removed from list."), style.name); + wxString message = GUI::format_wxstr(_L("Style '%1%' can't be used and will be removed from a list."), style.name); MessageDialog not_loaded_style_message(nullptr, message, title, wxOK); not_loaded_style_message.ShowModal(); m_style_manager.erase(*selected_style_index); @@ -2840,7 +2841,7 @@ void GLGizmoEmboss::draw_advanced() process(); } m_imgui->disabled_end(); // !can_use_surface - + // TRN EmbossGizmo: font units std::string units = _u8L("font points"); std::string units_fmt = "%.0f " + units; @@ -3111,9 +3112,9 @@ bool GLGizmoEmboss::choose_font_by_wxdialog() (!use_deserialized_font && !m_style_manager.load_style(emboss_style, wx_font))) { m_style_manager.erase(font_index); wxString message = GUI::format_wxstr( - _L("Font '%1%' can't be used. Please select another."), + "Font '%1%' can't be used. Please select another.", emboss_style.name); - wxString title = _L("Selected font is NOT True-type."); + wxString title = "Selected font is NOT True-type."; MessageDialog not_loaded_font_message(nullptr, message, title, wxOK); not_loaded_font_message.ShowModal(); return choose_font_by_wxdialog(); @@ -3150,7 +3151,7 @@ bool GLGizmoEmboss::choose_true_type_file() wxArrayString input_files; wxString fontDir = wxEmptyString; wxString selectedFile = wxEmptyString; - wxFileDialog dialog(nullptr, _L("Choose one or more files (TTF, TTC):"), + wxFileDialog dialog(nullptr, "Choose one or more files (TTF, TTC):", fontDir, selectedFile, file_wildcards(FT_FONTS), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() == wxID_OK) dialog.GetPaths(input_files); @@ -3178,7 +3179,7 @@ bool GLGizmoEmboss::choose_svg_file() wxArrayString input_files; wxString fontDir = wxEmptyString; wxString selectedFile = wxEmptyString; - wxFileDialog dialog(nullptr, _L("Choose SVG file:"), fontDir, + wxFileDialog dialog(nullptr, _L("Choose SVG file")+":", fontDir, selectedFile, file_wildcards(FT_SVG), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() == wxID_OK) dialog.GetPaths(input_files); @@ -3226,7 +3227,7 @@ void GLGizmoEmboss::create_notification_not_valid_font( } const std::string &face_name = face_name_opt.value_or(face_name_by_wx.value_or(es.path)); std::string text = - GUI::format(_L("Can't load exactly same font(\"%1%\"), " + GUI::format(_L("Can't load exactly same font(\"%1%\"). " "Aplication selected a similar one(\"%2%\"). " "You have to specify font for enable edit text."), face_name_3mf, face_name); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 0c89d7620..d5520403c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -44,7 +44,8 @@ bool GLGizmoFdmSupports::on_init() m_shortcut_key = WXK_CONTROL_L; m_desc["autopaint"] = _L("Automatic painting"); - m_desc["painting"] = _L("painting..."); + // TRN GizmoFdmSupports : message line during the waiting for autogenerated supports + m_desc["painting"] = _L("painting") + dots; m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Brush size") + ": "; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a3184371e..e8f6ad4af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1537,7 +1537,7 @@ void GLGizmoMeasure::render_dimensioning() m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); m_imgui->set_next_window_bg_alpha(0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + m_imgui->begin(wxString("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); ImGui::AlignTextToFramePadding(); ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -1736,7 +1736,7 @@ void GLGizmoMeasure::render_debug_dialog() add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); }; - m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin("Measure tool debug", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (ImGui::BeginTable("Mode", 2)) { std::string txt; switch (m_mode) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 90552c0a5..d60784758 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -318,6 +318,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_configuration.use_count = !m_configuration.use_count; start_process = true; } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && is_multipart) + // TRN %1% = "Detail level", %2% = "Decimate ratio" ImGui::SetTooltip("%s", GUI::format(_L( "Multipart object can be simplified only by %1%. " "If you want specify %2% process it separately."), @@ -539,7 +540,8 @@ void GLGizmoSimplify::apply_simplify() { const Selection& selection = m_parent.get_selection(); auto plater = wxGetApp().plater(); - plater->take_snapshot(_u8L("Simplify ") + create_volumes_name(m_volume_ids, selection)); + // TRN %1% = volumes name + plater->take_snapshot(Slic3r::format(_u8L("Simplify %1%"), create_volumes_name(m_volume_ids, selection))); plater->clear_before_change_mesh(selection.get_object_idx(), _u8L("Custom supports, seams and multimaterial painting were " "removed after simplifying the mesh.")); // After removing custom supports, seams, and multimaterial painting, we have to update info about the object to remove information about diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index f5c315b30..208cb2c0b 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -145,7 +145,7 @@ void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { if (!priv::finalize(canceled, eptr, m_input)) return; if (m_result.its.empty()) - return priv::create_message(_u8L("Can't create empty volume.")); + return priv::create_message("Can't create empty volume."); priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, m_input); } @@ -198,7 +198,7 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) // only for sure if (m_result.empty()) - return priv::create_message(_u8L("Can't create empty object.")); + return priv::create_message("Can't create empty object."); GUI_App &app = wxGetApp(); Plater *plater = app.plater(); @@ -462,8 +462,8 @@ TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl) if (was_canceled()) return {}; // only info ctl.call_on_main_thread([]() { - create_message(_u8L("It is used default volume for embossed " - "text, try to change text or font to fix it.")); + create_message("It is used default volume for embossed " + "text, try to change text or font to fix it."); }); } @@ -593,10 +593,10 @@ void priv::create_volume( // Parent object for text volume was propably removed. // Assumption: User know what he does, so text volume is no more needed. if (obj == nullptr) - return priv::create_message(_u8L("Bad object to create volume.")); + return priv::create_message("Bad object to create volume."); if (mesh.its.empty()) - return priv::create_message(_u8L("Can't create empty volume.")); + return priv::create_message("Can't create empty volume."); plater->take_snapshot(_L("Add Emboss text Volume")); @@ -823,10 +823,6 @@ bool priv::finalize(bool canceled, std::exception_ptr &eptr, const DataBase &inp return !process(eptr); } - -#include - void priv::create_message(const std::string &message) { - wxMessageBox(wxString(message), _L("Issue during embossing the text."), - wxOK | wxICON_WARNING); + show_error(nullptr, message.c_str()); } diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 2da3ad5b5..0b3c4cb28 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -163,7 +163,7 @@ void SLAImportJob::finalize(bool canceled, std::exception_ptr &eptr) p->plater->get_notification_manager()->push_notification( NotificationType::CustomNotification, NotificationManager::NotificationLevel::WarningNotificationLevel, - _u8L("The profile in the imported archive is corrupt and will not be loaded.")); + _u8L("The profile in the imported archive is corrupted and will not be loaded.")); } } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 071260ac2..dd2a90ebd 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1198,10 +1198,10 @@ static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, append_menu_item(view_menu, wxID_ANY, _L("Iso") + sep + "&0", _L("Iso View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("iso"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); view_menu->AppendSeparator(); - //TRN To be shown in the main menu View->Top + //TRN Main menu: View->Top append_menu_item(view_menu, wxID_ANY, _L("Top") + sep + "&1", _L("Top View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("top"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - //TRN To be shown in the main menu View->Bottom + //TRN Main menu: View->Bottom append_menu_item(view_menu, wxID_ANY, _L("Bottom") + sep + "&2", _L("Bottom View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("bottom"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); append_menu_item(view_menu, wxID_ANY, _L("Front") + sep + "&3", _L("Front View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("front"); }, @@ -1638,7 +1638,7 @@ void MainFrame::init_menubar_as_gcodeviewer() viewMenu = new wxMenu(); add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this)); viewMenu->AppendSeparator(); - append_menu_check_item(viewMenu, wxID_ANY, _L("Show legen&d") + sep + "L", _L("Show legend"), + append_menu_check_item(viewMenu, wxID_ANY, _L("Show Legen&d") + sep + "L", _L("Show legend"), [this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this, [this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this); } @@ -1777,7 +1777,7 @@ void MainFrame::quick_slice(const int qs) // show processbar dialog m_progress_dialog = new wxProgressDialog(_L("Slicing") + dots, - // TRN "Processing input_file_basename" + // TRN ProgressDialog on reslicing: "input file basename" format_wxstr(_L("Processing %s"), (input_file_basename + dots)), 100, nullptr, wxPD_AUTO_HIDE); m_progress_dialog->Pulse(); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index c96ed4dbc..3f7e58b7e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -2447,7 +2447,7 @@ void NotificationManager::push_download_URL_progress_notification(size_t id, con } } // push new one - NotificationData data{ NotificationType::URLDownload, NotificationLevel::ProgressBarNotificationLevel, 5, _u8L("Download:") + " " + text }; + NotificationData data{ NotificationType::URLDownload, NotificationLevel::ProgressBarNotificationLevel, 5, _u8L("Download") + ": " + text }; push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, id, user_action_callback), 0); } diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index d18df6bb2..a3cb345b4 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -173,8 +173,8 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) ConfigOptionDef option = opt.opt; // add label if any if (is_multioption_line && !option.label.empty()) { - //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 - label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? + // those two parameter names require localization with context + label = (option.label == "Top" || option.label == "Bottom") ? _CTX(option.label, "Layers") : _(option.label); label += ":"; @@ -622,9 +622,9 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) ConfigOptionDef option = opt.opt; // add label if any if (is_multioption_line && !option.label.empty()) { - //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 - label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? - _CTX(option.label, "Layers") : _(option.label); + // those two parameter names require localization with context + label = (option.label == "Top" || option.label == "Bottom") ? + _CTX(option.label, "Layers") : _(option.label); label += ":"; if (is_url_string) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index e567ac7c6..614fda913 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -384,8 +384,8 @@ void OptionsGroup::activate_line(Line& line) ConfigOptionDef option = opt.opt; // add label if any if ((option_set.size() > 1 || line.label.IsEmpty()) && !option.label.empty()) { - // To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 - wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? + // those two parameter names require localization with context + wxString str_label = (option.label == "Top" || option.label == "Bottom") ? _CTX(option.label, "Layers") : _(option.label); label = new wxStaticText(this->ctrl_parent(), wxID_ANY, str_label + ": ", wxDefaultPosition, //wxDefaultSize); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 08067c568..8732251e6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3033,8 +3033,8 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) ModelObject* obj = model.objects[obj_idx]; if (obj->is_cut()) { InfoDialog dialog(q, _L("Delete object which is a part of cut object"), - _L("You try to delete an object which is a part of a cut object.\n" - "This action will break a cut correspondence.\n" + _L("You try to delete an object which is a part of a cut object.") + "\n" + + _L("This action will break a cut information.\n" "After that PrusaSlicer can't guarantee model consistency"), false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING); dialog.SetButtonLabel(wxID_YES, _L("Delete object")); @@ -5437,7 +5437,7 @@ protected: LoadProjectsDialog::LoadProjectsDialog(const std::vector& paths) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, - format_wxstr(_L("%s - Multiple projects file"), SLIC3R_APP_NAME), wxDefaultPosition, + format_wxstr(_L("%1% - Multiple projects file"), SLIC3R_APP_NAME), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) { SetFont(wxGetApp().normal_font()); @@ -5826,7 +5826,7 @@ protected: ProjectDropDialog::ProjectDropDialog(const std::string& filename) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, - format_wxstr(_L("%s - Load project file"), SLIC3R_APP_NAME), wxDefaultPosition, + format_wxstr("%1% - %2%", SLIC3R_APP_NAME, _L("Load project file")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) { SetFont(wxGetApp().normal_font()); @@ -5934,7 +5934,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/* std::string filename = (*it).filename().string(); if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) { ProjectDropDialog::LoadType load_type = ProjectDropDialog::LoadType::Unknown; -// if (!model().objects.empty()) { // #ysFIXME_delete_after_test_of_6377 + { if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) || (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) load_type = ProjectDropDialog::LoadType::LoadGeometry; @@ -5951,11 +5951,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/* load_type = static_cast(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")), static_cast(ProjectDropDialog::LoadType::OpenProject), static_cast(ProjectDropDialog::LoadType::LoadConfig))); } -/* // #ysFIXME_delete_after_test_of_6377 } - else - load_type = ProjectDropDialog::LoadType::OpenProject; -*/ if (load_type == ProjectDropDialog::LoadType::Unknown) return false; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 0d5bf0f8c..492e3fc37 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -308,14 +308,9 @@ void PreferencesDialog::build() m_optgroup_general->append_separator(); append_bool_option(m_optgroup_general, "show_drop_project_dialog", -#if 1 // #ysFIXME_delete_after_test_of_6377 L("Show load project dialog"), L("When checked, whenever dragging and dropping a project file on the application or open it from a browser, " "shows a dialog asking to select the action to take on the file to load."), -#else - L("Show drop project dialog"), - L("When checked, whenever dragging and dropping a project file on the application, shows a dialog asking to select the action to take on the file to load."), -#endif app_config->get_bool("show_drop_project_dialog")); append_bool_option(m_optgroup_general, "single_instance", @@ -471,7 +466,7 @@ void PreferencesDialog::build() append_bool_option(m_optgroup_gui, "seq_top_layer_only", L("Sequential slider applied only to top layer"), - L("If enabled, changes made using the sequential slider, in preview, apply only to gcode top layer." + L("If enabled, changes made using the sequential slider, in preview, apply only to gcode top layer. " "If disabled, changes made using the sequential slider, in preview, apply to the whole gcode."), app_config->get_bool("seq_top_layer_only")); @@ -627,7 +622,7 @@ void PreferencesDialog::build() append_bool_option(m_optgroup_dark_mode, "sys_menu_enabled", L("Use system menu for application"), L("If enabled, application will use the standard Windows system menu,\n" - "but on some combination od display scales it can look ugly. If disabled, old UI will be used."), + "but on some combination of display scales it can look ugly. If disabled, old UI will be used."), app_config->get_bool("sys_menu_enabled")); } diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index b381024aa..74bbe2ada 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -71,7 +71,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo if (combo_storage != nullptr) { // PrusaLink specific: User needs to choose a storage - auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage:")); + auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage") + ":"); content_sizer->Add(label_group); content_sizer->Add(combo_storage, 0, wxBOTTOM, 2 * VERT_SPACING); combo_storage->SetValue(storage_names.front()); @@ -80,7 +80,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo combo_storage->SetValue(recent_storage); } else if (storage_names.GetCount() == 1){ // PrusaLink specific: Show which storage has been detected. - auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage: ") + storage_names.front()); + auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage") + ": " + storage_names.front()); content_sizer->Add(label_group); m_preselected_storage = storage_paths.front(); } diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index d4c2ba58c..33f41b9c4 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -87,9 +87,13 @@ void SavePresetDialog::Item::init_input_name_ctrl(wxBoxSizer *input_name_sizer, wxString SavePresetDialog::Item::get_top_label_text() const { - const std::string label_str = m_use_text_ctrl ?_u8L("Rename %s to:") : _u8L("Save %s as:"); + const std::string label_str = m_use_text_ctrl ? + // TRN %1% = "Preset" + L("Rename %1% to") : + // TRN %1% = "Preset" + L("Save %1% as"); Tab* tab = wxGetApp().get_tab(m_type); - return from_u8((boost::format(label_str) % into_u8(tab->title())).str()); + return format_wxstr(_(label_str) + ":", tab->title()); } SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent): @@ -324,6 +328,7 @@ void SavePresetDialog::build(std::vector types, std::string suffix #endif // __WXMSW__ if (suffix.empty()) + // TRN Suffix for the preset name. Have to be a noun. suffix = _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index ecee0a4e0..a202ae5de 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -5,6 +5,7 @@ #include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/CameraUtils.hpp" +#include "slic3r/GUI/I18N.hpp" #include "libslic3r/Emboss.hpp" namespace Slic3r::GUI { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3cdd84662..6382fb0ae 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -163,9 +163,10 @@ void Tab::create_preset_tab() add_scaled_button(panel, &m_btn_hide_incompatible_presets, "flag_green"); m_btn_compare_preset->SetToolTip(_L("Compare this preset with some another")); - // TRN "Save current Settings" + // TRN Settings Tabs: Tooltip for save button: "Settings" m_btn_save_preset->SetToolTip(format_wxstr(_L("Save current %s"), m_title)); - m_btn_rename_preset->SetToolTip(format_wxstr(_L("Rename current %s"), m_title)); + // TRN Settings Tabs: Tooltip for rename button: "Settings" + m_btn_rename_preset->SetToolTip(format_wxstr(_L("Rename current %1%"), m_title)); m_btn_rename_preset->Hide(); m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); m_btn_delete_preset->Hide(); @@ -3925,7 +3926,6 @@ void Tab::delete_preset() auto current_preset = m_presets->get_selected_preset(); // Don't let the user delete the ' - default - ' configuration. wxString action = current_preset.is_external ? _L("remove") : _L("delete"); - // TRN remove/delete PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; wxString msg; @@ -3969,12 +3969,13 @@ void Tab::delete_preset() "Note, that these printers will be deleted after deleting the selected preset.", ph_printers_only.size()) + "\n\n"; } } - + + // TRN "remove/delete" msg += from_u8((boost::format(_u8L("Are you sure you want to %1% the selected preset?")) % action).str()); } action = current_preset.is_external ? _L("Remove") : _L("Delete"); - // TRN Remove/Delete + // TRN Settings Tabs: Button in toolbar: "Remove/Delete" wxString title = format_wxstr(_L("%1% Preset"), action); if (current_preset.is_default || //wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 0d4dc576d..dea01eb8f 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -135,10 +135,10 @@ bool AppUpdateAvailableDialog::disable_version_check() const // AppUpdateDownloadDialog AppUpdateDownloadDialog::AppUpdateDownloadDialog( const Semver& ver_online, boost::filesystem::path& path) - : MsgDialog(nullptr, _(L("App Update download")), wxString::Format(_(L("New version of %s is available.")), SLIC3R_APP_NAME)) + : MsgDialog(nullptr, _L("App Update download"), format_wxstr(_L("New version of %1% is available."), SLIC3R_APP_NAME)) { auto* versions = new wxFlexGridSizer(2, 0, VERT_SPACING); - versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:")))); + versions->Add(new wxStaticText(this, wxID_ANY, _L("New version") + ":")); versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string())); content_sizer->Add(versions); content_sizer->AddSpacer(VERT_SPACING); @@ -148,7 +148,7 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( const Semver& ver_online, boos #endif content_sizer->AddSpacer(VERT_SPACING); content_sizer->AddSpacer(VERT_SPACING); - content_sizer->Add(new wxStaticText(this, wxID_ANY, _(L("Target directory:")))); + content_sizer->Add(new wxStaticText(this, wxID_ANY, _L("Target directory") + ":")); content_sizer->AddSpacer(VERT_SPACING); txtctrl_path = new wxTextCtrl(this, wxID_ANY, GUI::format_wxstr(path.parent_path().string())); filename = GUI::format_wxstr(path.filename().string()); @@ -173,7 +173,7 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( const Semver& ver_online, boos dir = GUI::format(txtctrl_path->GetValue()); wxDirDialog save_dlg( this - , _L("Select directory:") + , _L("Select directory") + ":" , GUI::format_wxstr(dir.string()) /* , filename //boost::nowide::widen(AppUpdater::get_filename_from_url(txtctrl_path->GetValue().ToUTF8().data())) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index d8d977c05..540852af8 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -769,12 +769,17 @@ bool PrusaLink::get_storage(wxArrayString& storage_path, wxArrayString& storage_ if (res && storage_path.empty()) { if (!storage.empty()) { // otherwise error_msg is already filled - error_msg = L"\n\n" + _L("Storages found:") + L" \n"; + error_msg = L"\n\n" + _L("Storages found") + L": \n"; for (const auto& si : storage) { - error_msg += si.path + L" : " + (si.read_only ? _L("read only") : _L("no free space")) + L"\n"; + error_msg += GUI::format_wxstr(si.read_only ? + // TRN %1% = storage path + _L("%1% : read only") : + // TRN %1% = storage path + _L("%1% : no free space"), si.path) + L"\n"; } } - std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%.%2%"), m_host, error_msg); + // TRN %1% = host + std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%."), m_host) + GUI::into_u8(error_msg); BOOST_LOG_TRIVIAL(error) << message; throw Slic3r::IOError(message); } @@ -1138,9 +1143,7 @@ wxString PrusaConnect::get_test_ok_msg() const wxString PrusaConnect::get_test_failed_msg(wxString& msg) const { - return GUI::from_u8((boost::format("%s: %s") - % _utf8(L("Could not connect to PrusaConnect")) - % std::string(msg.ToUTF8())).str()); + return GUI::format_wxstr("%s: %s", _L("Could not connect to PrusaConnect"), msg); } } From f57de436804a881a4bc45796a23a4dfd0eb5d2a3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 7 Mar 2023 15:53:39 +0100 Subject: [PATCH 144/201] Fixed several more warnings --- src/libslic3r/Geometry/VoronoiUtilsCgal.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/GUI_Factories.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 4 ++-- src/slic3r/GUI/Plater.cpp | 3 ++- src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 ++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp index c3348110b..832152c5f 100644 --- a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp +++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp @@ -128,7 +128,7 @@ inline static Linef make_linef(const VD::edge_type &edge) return {Vec2d(v0->x(), v0->y()), Vec2d(v1->x(), v1->y())}; } -inline static bool is_equal(const VD::vertex_type &first, const VD::vertex_type &second) { return first.x() == second.x() && first.y() == second.y(); } +[[maybe_unused]] inline static bool is_equal(const VD::vertex_type &first, const VD::vertex_type &second) { return first.x() == second.x() && first.y() == second.y(); } // FIXME Lukas H.: Also includes parabolic segments. bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_diagram) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 22f296456..ed1a08b57 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2705,7 +2705,7 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(1)); - auto support_material_interface_layers = def = this->add("support_material_interface_layers", coInt); + def = this->add("support_material_interface_layers", coInt); def->label = L("Top interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material."); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 4887d3dcf..60049c865 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -1000,7 +1000,7 @@ void MenuFactory::append_menu_item_edit_text(wxMenu *menu) std::string icon = ""; append_menu_item( menu, wxID_ANY, name, description, - [can_edit_text](wxCommandEvent &) { + [](wxCommandEvent &) { plater()->canvas3D()->get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss); }, icon, nullptr, can_edit_text, m_parent); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index dd2a90ebd..0c7805db8 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -314,9 +314,9 @@ void MainFrame::bind_diff_dialog() process(diff_dlg_type); }; - diff_dialog.Bind(EVT_DIFF_DIALOG_TRANSFER, [this, process_options, transfer](SimpleEvent&) { process_options(transfer); }); + diff_dialog.Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); }); - diff_dialog.Bind(EVT_DIFF_DIALOG_UPDATE_PRESETS,[this, process_options, update_presets](SimpleEvent&) { process_options(update_presets); }); + diff_dialog.Bind(EVT_DIFF_DIALOG_UPDATE_PRESETS,[process_options, update_presets](SimpleEvent&) { process_options(update_presets); }); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8732251e6..3595276af 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -830,6 +830,7 @@ Sidebar::Sidebar(Plater *parent) wxRIGHT, margin_5); #else wxBOTTOM, 1); + (void)margin_5; // supress unused capture warning #endif // __WXGTK3__ } else { sizer_filaments->Add(combo_and_btn_sizer, 0, wxEXPAND | @@ -2251,7 +2252,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->load_files(input_files); }); - this->q->Bind(EVT_START_DOWNLOAD_OTHER_INSTANCE, [this](StartDownloadOtherInstanceEvent& evt) { + this->q->Bind(EVT_START_DOWNLOAD_OTHER_INSTANCE, [](StartDownloadOtherInstanceEvent& evt) { BOOST_LOG_TRIVIAL(trace) << "Received url from other instance event."; wxGetApp().mainframe->Raise(); for (size_t i = 0; i < evt.data.size(); ++i) { diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 8e3fa7874..d7d4798c2 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1591,7 +1591,7 @@ void DiffPresetDialog::create_buttons() } evt.Enable(enable); }); - m_transfer_btn->Bind(wxEVT_ENTER_WINDOW, [this, show_in_bottom_info](wxMouseEvent& e) { + m_transfer_btn->Bind(wxEVT_ENTER_WINDOW, [show_in_bottom_info](wxMouseEvent& e) { show_in_bottom_info(_L("Transfer the selected options from left preset to the right.\n" "Note: New modified presets will be selected in settings tabs after close this dialog."), e); }); @@ -1599,7 +1599,7 @@ void DiffPresetDialog::create_buttons() m_save_btn = new ScalableButton(this, wxID_ANY, "save", _L("Save"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, 24); m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { button_event(Action::Save); }); m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_tree->has_selection()); }); - m_save_btn->Bind(wxEVT_ENTER_WINDOW, [this, show_in_bottom_info](wxMouseEvent& e) { + m_save_btn->Bind(wxEVT_ENTER_WINDOW, [show_in_bottom_info](wxMouseEvent& e) { show_in_bottom_info(_L("Save the selected options from left preset to the right."), e); }); // Cancel From 582f51cdfd9360cf81dc82843a838faf7a87b4dd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 24 Mar 2023 08:36:43 +0100 Subject: [PATCH 145/201] Fixed crash when opening Emboss Gizmo pressing 'T' key after slicing an object using SLA printer --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index dd18cb878..a4ba6de32 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3444,7 +3444,7 @@ void priv::find_closest_volume(const Selection &selection, for (unsigned int id : indices) { const GLVolume *gl_volume = selection.get_volume(id); const ModelVolume *volume = get_model_volume(*gl_volume, objects); - if (!volume->is_model_part()) continue; + if (volume == nullptr || !volume->is_model_part()) continue; Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume); Vec2d c = hull.centroid().cast(); Vec2d d = c - screen_center; From 6ba067ad36b10641744d88423ce29bbea85b2256 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 24 Mar 2023 08:40:32 +0100 Subject: [PATCH 146/201] Fixed crash when trying to add text on an object sliced using SLA printer by right clicking on supports or pad --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index a4ba6de32..215517470 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3394,6 +3394,7 @@ bool priv::start_create_volume_on_surface_job( { assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; + if (gl_volume->volume_idx() < 0) return false; Plater *plater = wxGetApp().plater(); const ModelObjectPtrs &objects = plater->model().objects; From 5d89e742b41ba5e38f8b3b5dbeb648aceecd6c54 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 24 Mar 2023 09:27:12 +0100 Subject: [PATCH 147/201] Added missed includes of "format.hpp" --- src/slic3r/GUI/ConfigSnapshotDialog.cpp | 1 + src/slic3r/GUI/OpenGLManager.cpp | 1 + src/slic3r/GUI/wxExtensions.cpp | 1 + src/slic3r/Utils/AstroBox.cpp | 1 + src/slic3r/Utils/Duet.cpp | 1 + src/slic3r/Utils/FlashAir.cpp | 1 + src/slic3r/Utils/MKS.cpp | 1 + 7 files changed, 7 insertions(+) diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index c1817a3ba..77193c857 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -9,6 +9,7 @@ #include "GUI_App.hpp" #include "MainFrame.hpp" #include "wxExtensions.hpp" +#include "format.hpp" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 6960d0a75..447da442e 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -7,6 +7,7 @@ #endif // ENABLE_GL_CORE_PROFILE #include "I18N.hpp" #include "3DScene.hpp" +#include "format.hpp" #include "libslic3r/Platform.hpp" diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index f67ad287a..a8da90024 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -18,6 +18,7 @@ #include "BitmapComboBox.hpp" #include "libslic3r/Utils.hpp" #include "OG_CustomCtrl.hpp" +#include "format.hpp" #include "libslic3r/Color.hpp" diff --git a/src/slic3r/Utils/AstroBox.cpp b/src/slic3r/Utils/AstroBox.cpp index e7044ad20..46bb69b20 100644 --- a/src/slic3r/Utils/AstroBox.cpp +++ b/src/slic3r/Utils/AstroBox.cpp @@ -14,6 +14,7 @@ #include "libslic3r/PrintConfig.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/format.hpp" #include "Http.hpp" diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 74bf080cd..e77ebc858 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -20,6 +20,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/format.hpp" #include "Http.hpp" namespace fs = boost::filesystem; diff --git a/src/slic3r/Utils/FlashAir.cpp b/src/slic3r/Utils/FlashAir.cpp index 157d9623f..51fd6b904 100644 --- a/src/slic3r/Utils/FlashAir.cpp +++ b/src/slic3r/Utils/FlashAir.cpp @@ -19,6 +19,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/format.hpp" #include "Http.hpp" namespace fs = boost::filesystem; diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index cce212631..826476757 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -24,6 +24,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/format.hpp" #include "Http.hpp" namespace fs = boost::filesystem; From cf226f8eabd513f31ea3dd41631547f1e23567b9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 8 Mar 2023 19:34:41 +0100 Subject: [PATCH 148/201] Added 'travel_acceleration' configuration option (not used so far) --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 10 +++++++++- src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 3598d36ae..21b712217 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -434,7 +434,7 @@ static std::vector s_Preset_print_options { "enable_dynamic_overhang_speeds", "overhang_speed_0", "overhang_speed_1", "overhang_speed_2", "overhang_speed_3", "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration", - "external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration", + "external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration", "travel_acceleration", "bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", "min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0dd20ea93..afc039bdd 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -137,6 +137,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "start_filament_gcode", "toolchange_gcode", "top_solid_infill_acceleration", + "travel_acceleration", "thumbnails", "thumbnails_format", "use_firmware_retraction", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ed1a08b57..ce5aa69b5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1463,7 +1463,15 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0)); - + + def = this->add("travel_acceleration", coFloat); + def->label = L("Travel"); + def->tooltip = L("This is the acceleration your printer will use for travel moves. Set zero to disable " + "acceleration control for travel."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0)); def = this->add("infill_every_layers", coInt); def->label = L("Combine infill every"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 52924bf91..17f5e501c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -815,6 +815,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionPoints, thumbnails)) ((ConfigOptionEnum, thumbnails_format)) ((ConfigOptionFloat, top_solid_infill_acceleration)) + ((ConfigOptionFloat, travel_acceleration)) ((ConfigOptionBools, wipe)) ((ConfigOptionBool, wipe_tower)) ((ConfigOptionFloat, wipe_tower_x)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 1bc2280d5..4c5b0fd8e 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -256,7 +256,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_default_acceleration = config->opt_float("default_acceleration") > 0; for (auto el : { "perimeter_acceleration", "infill_acceleration", "top_solid_infill_acceleration", - "solid_infill_acceleration", "external_perimeter_acceleration" + "solid_infill_acceleration", "external_perimeter_acceleration", "bridge_acceleration", "first_layer_acceleration" }) toggle_field(el, have_default_acceleration); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6382fb0ae..dc3100eb2 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1573,6 +1573,7 @@ void TabPrint::build() optgroup->append_single_option_line("bridge_acceleration"); optgroup->append_single_option_line("first_layer_acceleration"); optgroup->append_single_option_line("first_layer_acceleration_over_raft"); + optgroup->append_single_option_line("travel_acceleration"); optgroup->append_single_option_line("default_acceleration"); optgroup = page->new_optgroup(L("Autospeed (advanced)")); From 3ff600bed07770d7afc5c59c0cf43061a831dd21 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Mar 2023 09:14:54 +0100 Subject: [PATCH 149/201] Separated travel and print acceleration control --- src/libslic3r/GCode.cpp | 18 ++++++++++++---- src/libslic3r/GCodeWriter.cpp | 39 +++++++++++++++++------------------ src/libslic3r/GCodeWriter.hpp | 15 ++++++++++++-- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 2fca25c96..a9d7da76d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2547,7 +2547,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr } // reset acceleration - gcode += m_writer.set_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5)); + gcode += m_writer.set_print_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5)); if (m_wipe.enable) { m_wipe.path = paths.front().polyline; @@ -2633,7 +2633,7 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::s } } // reset acceleration - gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); + gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; } @@ -2659,7 +2659,7 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string_view description m_wipe.path.reverse(); } // reset acceleration - gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); + gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; } @@ -2802,7 +2802,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } else { acceleration = m_config.default_acceleration.value; } - gcode += m_writer.set_acceleration((unsigned int)floor(acceleration + 0.5)); + gcode += m_writer.set_print_acceleration((unsigned int)floor(acceleration + 0.5)); } // calculate extrusion length per distance unit @@ -3071,8 +3071,18 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string // use G1 because we rely on paths being straight (G0 may make round paths) if (travel.size() >= 2) { + + gcode += m_writer.set_travel_acceleration((unsigned int)(m_config.travel_acceleration.value + 0.5)); + for (size_t i = 1; i < travel.size(); ++ i) gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment); + + if (! m_writer.supports_PT()) { + // In case that this flavor does not support separate print and travel acceleration, + // reset acceleration to default. + gcode += m_writer.set_travel_acceleration((unsigned int)(m_config.travel_acceleration.value + 0.5)); + } + this->set_last_pos(travel.points.back()); } return gcode; diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index c2caf5766..b323ae8b2 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -15,6 +15,11 @@ namespace Slic3r { +bool GCodeWriter::supports_PT() const +{ + return (FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfMarlinFirmware) || FLAVOR_IS(gcfRepRapFirmware)); +} + void GCodeWriter::apply_print_config(const PrintConfig &print_config) { this->config.apply(print_config, true); @@ -154,36 +159,30 @@ std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait return gcode.str(); } -std::string GCodeWriter::set_acceleration(unsigned int acceleration) +std::string GCodeWriter::set_acceleration_internal(Acceleration type, unsigned int acceleration) { // Clamp the acceleration to the allowed maximum. + // TODO: What about max travel acceleration ? Currently it is clamped by the extruding acceleration !!! if (m_max_acceleration > 0 && acceleration > m_max_acceleration) acceleration = m_max_acceleration; - if (acceleration == 0 || acceleration == m_last_acceleration) + // Are we setting travel acceleration for a flavour that supports separate travel and print acc? + bool separate_travel = (type == Acceleration::Travel && supports_PT()); + + auto& last_value = separate_travel ? m_last_travel_acceleration : m_last_acceleration ; + if (acceleration == 0 || acceleration == last_value) return std::string(); - m_last_acceleration = acceleration; + last_value = acceleration; std::ostringstream gcode; - if (FLAVOR_IS(gcfRepetier)) { - // M201: Set max printing acceleration - gcode << "M201 X" << acceleration << " Y" << acceleration; - if (this->config.gcode_comments) gcode << " ; adjust acceleration"; - gcode << "\n"; - // M202: Set max travel acceleration - gcode << "M202 X" << acceleration << " Y" << acceleration; - } else if (FLAVOR_IS(gcfRepRapFirmware)) { - // M204: Set default acceleration - gcode << "M204 P" << acceleration; - } else if (FLAVOR_IS(gcfMarlinFirmware)) { - // This is new MarlinFirmware with separated print/retraction/travel acceleration. - // Use M204 P, we don't want to override travel acc by M204 S (which is deprecated anyway). - gcode << "M204 P" << acceleration; - } else { - // M204: Set default acceleration + if (FLAVOR_IS(gcfRepetier)) + gcode << (separate_travel ? "M202 X" : "M201 X") << acceleration << " Y" << acceleration; + else if (FLAVOR_IS(gcfRepRapFirmware) || FLAVOR_IS(gcfMarlinFirmware)) + gcode << (separate_travel ? "M204 T" : "M204 P") << acceleration; + else gcode << "M204 S" << acceleration; - } + if (this->config.gcode_comments) gcode << " ; adjust acceleration"; gcode << "\n"; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 6c36c0d3a..2a2a488fa 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -43,7 +43,8 @@ public: std::string postamble() const; std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; std::string set_bed_temperature(unsigned int temperature, bool wait = false); - std::string set_acceleration(unsigned int acceleration); + std::string set_print_acceleration(unsigned int acceleration) { return set_acceleration_internal(Acceleration::Print, acceleration); } + std::string set_travel_acceleration(unsigned int acceleration) { return set_acceleration_internal(Acceleration::Travel, acceleration); } std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; // return false if this extruder was already selected @@ -69,6 +70,9 @@ public: std::string unlift(); Vec3d get_position() const { return m_pos; } + // Returns whether this flavor supports separate print and travel acceleration. + bool supports_PT() const; + // To be called by the CoolingBuffer from another thread. static std::string set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed); // To be called by the main thread. It always emits the G-code, it does not remember the previous state. @@ -81,7 +85,8 @@ private: std::string m_extrusion_axis; bool m_single_extruder_multi_material; Extruder* m_extruder; - unsigned int m_last_acceleration; + unsigned int m_last_acceleration = (unsigned int)(-1); + unsigned int m_last_travel_acceleration = (unsigned int)(-1); // only used for flavors supporting separate print/travel acc // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware. // If set to zero, the limit is not in action. unsigned int m_max_acceleration; @@ -90,8 +95,14 @@ private: double m_lifted; Vec3d m_pos = Vec3d::Zero(); + enum class Acceleration { + Travel, + Print + }; + std::string _travel_to_z(double z, const std::string &comment); std::string _retract(double length, double restart_extra, const std::string &comment); + std::string set_acceleration_internal(Acceleration type, unsigned int acceleration); }; class GCodeFormatter { From 42f1217f6eb12783f9a0c864e62e6cecb1200527 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Mar 2023 14:05:04 +0100 Subject: [PATCH 150/201] Klipper: Adding the new firmware flavor: - added the new option - allowed machine limits page - added a check for Klipper + 'emit limits to gcode' (Print::validate so far) --- src/libslic3r/Print.cpp | 4 ++++ src/libslic3r/PrintConfig.cpp | 7 +++++-- src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/GUI/Tab.cpp | 5 +++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index afc039bdd..febc4956d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -502,6 +502,10 @@ std::string Print::validate(std::string* warning) const return _u8L("The Spiral Vase option can only be used when printing single material objects."); } + if (m_config.machine_limits_usage == MachineLimitsUsage::EmitToGCode && m_config.gcode_flavor == gcfKlipper) + return L("Machine limits cannot be emitted to G-Code when Klipper firmware flavor is used. " + "Change the value of machine_limits_usage."); + // Cache of layer height profiles for checking: // 1) Whether all layers are synchronized if printing with wipe tower and / or unsynchronized supports. // 2) Whether layer height is constant for Organic supports. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ce5aa69b5..1adaa5430 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -48,6 +48,7 @@ static const t_config_enum_values s_keys_map_GCodeFlavor { { "makerware", gcfMakerWare }, { "marlin", gcfMarlinLegacy }, { "marlin2", gcfMarlinFirmware }, + { "klipper", gcfKlipper }, { "sailfish", gcfSailfish }, { "smoothie", gcfSmoothie }, { "mach3", gcfMach3 }, @@ -1406,6 +1407,7 @@ void PrintConfigDef::init_fff_params() { "makerware", "MakerWare (MakerBot)" }, { "marlin", "Marlin (legacy)" }, { "marlin2", "Marlin 2" }, + { "klipper", "Klipper" }, { "sailfish", "Sailfish (MakerBot)" }, { "mach3", "Mach3/LinuxCNC" }, { "machinekit", "Machinekit" }, @@ -4421,8 +4423,9 @@ std::string validate(const FullPrintConfig &cfg) cfg.gcode_flavor.value != gcfMarlinLegacy && cfg.gcode_flavor.value != gcfMarlinFirmware && cfg.gcode_flavor.value != gcfMachinekit && - cfg.gcode_flavor.value != gcfRepetier) - return "--use-firmware-retraction is only supported by Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware"; + cfg.gcode_flavor.value != gcfRepetier && + cfg.gcode_flavor.value != gcfKlipper) + return "--use-firmware-retraction is only supported by Marlin, Klipper, Smoothie, RepRapFirmware, Repetier and Machinekit firmware"; if (cfg.use_firmware_retraction.value) for (unsigned char wipe : cfg.wipe.values) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 17f5e501c..3a602b7ac 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -32,7 +32,7 @@ namespace Slic3r { enum GCodeFlavor : unsigned char { - gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfSailfish, gcfMach3, gcfMachinekit, + gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfKlipper, gcfSailfish, gcfMach3, gcfMachinekit, gcfSmoothie, gcfNoExtrusion, }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index dc3100eb2..6227fc537 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2866,7 +2866,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/) { size_t n_before_extruders = 2; // Count of pages before Extruder pages auto flavor = m_config->option>("gcode_flavor")->value; - bool show_mach_limits = (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware); + bool show_mach_limits = (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware || flavor == gcfKlipper); /* ! Freeze/Thaw in this function is needed to avoid call OnPaint() for erased pages * and be cause of application crash, when try to change Preset in moment, @@ -3102,7 +3102,8 @@ void TabPrinter::toggle_options() if (m_active_page->title() == "Machine limits" && m_machine_limits_description_line) { assert(flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware - || flavor == gcfRepRapFirmware); + || flavor == gcfRepRapFirmware + || flavor == gcfKlipper); const auto *machine_limits_usage = m_config->option>("machine_limits_usage"); bool enabled = machine_limits_usage->value != MachineLimitsUsage::Ignore; bool silent_mode = m_config->opt_bool("silent_mode"); From b9eb13dff917db302d03aa7278c9cc9801fea0a1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Mar 2023 15:03:32 +0100 Subject: [PATCH 151/201] Klipper: specific behaviour (GCodeProcessor, WipeTower). GCodeWriter should behave as Marlin(legacy). --- src/libslic3r/GCode/GCodeProcessor.cpp | 17 +++++++++-------- src/libslic3r/GCode/WipeTower.cpp | 11 ++++++++--- src/libslic3r/GCodeWriter.cpp | 1 + src/libslic3r/Print.cpp | 5 +++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 71d723ca3..ead792662 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -568,10 +568,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_result.filament_cost[i] = static_cast(config.filament_cost.get_at(i)); } - if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfRepRapFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) { + if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfRepRapFirmware || m_flavor == gcfKlipper) + && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) { m_time_processor.machine_limits = reinterpret_cast(config); - if (m_flavor == gcfMarlinLegacy) { - // Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead. + if (m_flavor == gcfMarlinLegacy || m_flavor == gcfKlipper) { + // Legacy Marlin and Klipper don't have separate travel acceleration, they use the 'extruding' value instead. m_time_processor.machine_limits.machine_max_acceleration_travel = m_time_processor.machine_limits.machine_max_acceleration_extruding; } if (m_flavor == gcfRepRapFirmware) { @@ -788,7 +789,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (machine_limits_usage != nullptr) use_machine_limits = machine_limits_usage->value != MachineLimitsUsage::Ignore; - if (use_machine_limits && (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfRepRapFirmware)) { + if (use_machine_limits && (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfRepRapFirmware || m_flavor == gcfKlipper)) { const ConfigOptionFloats* machine_max_acceleration_x = config.option("machine_max_acceleration_x"); if (machine_max_acceleration_x != nullptr) m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values; @@ -846,8 +847,8 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values; - // Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead. - const ConfigOptionFloats* machine_max_acceleration_travel = config.option(m_flavor == gcfMarlinLegacy + // Legacy Marlin and Klipper don't have separate travel acceleration, they use the 'extruding' value instead. + const ConfigOptionFloats* machine_max_acceleration_travel = config.option((m_flavor == gcfMarlinLegacy || m_flavor == gcfKlipper) ? "machine_max_acceleration_extruding" : "machine_max_acceleration_travel"); if (machine_max_acceleration_travel != nullptr) @@ -885,7 +886,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION; } - if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) { + if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) { // No Klipper here, it does not support silent mode. const ConfigOptionBool* silent_mode = config.option("silent_mode"); if (silent_mode != nullptr) { if (silent_mode->value && m_time_processor.machine_limits.machine_max_acceleration_x.values.size() > 1) @@ -3244,7 +3245,7 @@ void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M220(const GCodeReader::GCodeLine& line) { - if (m_flavor != gcfMarlinLegacy && m_flavor != gcfMarlinFirmware) + if (m_flavor != gcfMarlinLegacy && m_flavor != gcfMarlinFirmware && m_flavor != gcfKlipper) return; if (line.has('B')) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 58af2da48..7fdbb6915 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -93,9 +93,12 @@ public: } WipeTowerWriter& disable_linear_advance() { - m_gcode += (m_gcode_flavor == gcfRepRapSprinter || m_gcode_flavor == gcfRepRapFirmware - ? (std::string("M572 D") + std::to_string(m_current_tool) + " S0\n") - : std::string("M900 K0\n")); + if (m_gcode_flavor == gcfRepRapSprinter || m_gcode_flavor == gcfRepRapFirmware) + m_gcode += (std::string("M572 D") + std::to_string(m_current_tool) + " S0\n"); + else if (m_gcode_flavor == gcfKlipper) + m_gcode += "SET_PRESSURE_ADVANCE ADVANCE=0\n"; + else + m_gcode += "M900 K0\n"; return *this; } @@ -363,6 +366,8 @@ public: // Set digital trimpot motor WipeTowerWriter& set_extruder_trimpot(int current) { + if (m_gcode_flavor == gcfKlipper) + return *this; if (m_gcode_flavor == gcfRepRapSprinter || m_gcode_flavor == gcfRepRapFirmware) m_gcode += "M906 E"; else diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index b323ae8b2..45176baaa 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -58,6 +58,7 @@ std::string GCodeWriter::preamble() FLAVOR_IS(gcfRepRapFirmware) || FLAVOR_IS(gcfMarlinLegacy) || FLAVOR_IS(gcfMarlinFirmware) || + FLAVOR_IS(gcfKlipper) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfSmoothie)) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index febc4956d..2afe02517 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -562,8 +562,9 @@ std::string Print::validate(std::string* warning) const } if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware && - m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlinLegacy && m_config.gcode_flavor != gcfMarlinFirmware) - return _u8L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors."); + m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlinLegacy && + m_config.gcode_flavor != gcfMarlinFirmware && m_config.gcode_flavor != gcfKlipper) + return _u8L("The Wipe Tower is currently only supported for the Marlin, Klipper, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors."); if (! m_config.use_relative_e_distances) return _u8L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); if (m_config.ooze_prevention && m_config.single_extruder_multi_material) From 06bccab711a055d269dbd92df7888722bcce28dc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Mar 2023 15:54:55 +0100 Subject: [PATCH 152/201] Added Mainsail print host --- src/libslic3r/PrintConfig.cpp | 2 ++ src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/Utils/OctoPrint.hpp | 10 ++++++++++ src/slic3r/Utils/PrintHost.cpp | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1adaa5430..62f9a9a2f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -68,6 +68,7 @@ static const t_config_enum_values s_keys_map_PrintHostType { { "prusalink", htPrusaLink }, { "prusaconnect", htPrusaConnect }, { "octoprint", htOctoPrint }, + { "mainsail", htMainSail }, { "duet", htDuet }, { "flashair", htFlashAir }, { "astrobox", htAstroBox }, @@ -1954,6 +1955,7 @@ void PrintConfigDef::init_fff_params() { "prusalink", "PrusaLink" }, { "prusaconnect", "PrusaConnect" }, { "octoprint", "OctoPrint" }, + { "mainsail", "Mainsail/Fluidd" }, { "duet", "Duet" }, { "flashair", "FlashAir" }, { "astrobox", "AstroBox" }, diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3a602b7ac..265628d78 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -44,7 +44,7 @@ enum class MachineLimitsUsage { }; enum PrintHostType { - htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS + htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htMainSail }; enum AuthorizationType { diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index fd558eb2c..82c07f6f4 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -139,6 +139,16 @@ protected: void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override; }; + +class Mainsail : public OctoPrint +{ +public: + Mainsail(DynamicPrintConfig* config) : OctoPrint(config) {} + ~Mainsail() override = default; + + const char* get_name() const override { return "Mainsail/Fluidd"; } +}; + } #endif diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index c8f0e34bc..5cb318715 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -54,6 +54,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) case htPrusaLink: return new PrusaLink(config); case htPrusaConnect: return new PrusaConnect(config); case htMKS: return new MKS(config); + case htMainSail: return new Mainsail(config); default: return nullptr; } } else { From da714c7210d15a259a3d23e22d4810a752f8e1ac Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Mar 2023 11:29:19 +0100 Subject: [PATCH 153/201] Removed M107 inserted automatically at the start of the G-Code --- src/libslic3r/GCode.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a9d7da76d..1135220df 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1147,10 +1147,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Emit machine envelope limits for the Marlin firmware. this->print_machine_envelope(file, print); - // Disable fan. - if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id)) - file.write(m_writer.set_fan(0)); - // Let the start-up script prime the 1st printing tool. m_placeholder_parser.set("initial_tool", initial_extruder_id); m_placeholder_parser.set("initial_extruder", initial_extruder_id); From 09122fb0d04ae70e2c787ef2523dbb7c9ee7eaaa Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Mar 2023 14:31:05 +0100 Subject: [PATCH 154/201] Added a new config option to disable automatic temperature commands around start gcode (autoemit_temperature_commands) --- src/libslic3r/GCode.cpp | 10 ++++++---- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 24 +++++++++++++++++------- src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/Tab.cpp | 3 +++ 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1135220df..fcfd83da0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1706,17 +1706,18 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) // M190 - Set Extruder Temperature and Wait void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { + bool autoemit = print.config().autoemit_temperature_commands; // Initial bed temperature based on the first extruder. int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id); // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, false, temp_by_gcode); - if (temp_set_by_gcode && temp_by_gcode >= 0 && temp_by_gcode < 1000) + if (autoemit && temp_set_by_gcode && temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if // the custom start G-code emited these. std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait); - if (! temp_set_by_gcode) + if (autoemit && ! temp_set_by_gcode) file.write(set_temp_gcode); } @@ -1727,13 +1728,14 @@ void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &p // RepRapFirmware: G10 Sxx void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { + bool autoemit = print.config().autoemit_temperature_commands; // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; bool include_g10 = print.config().gcode_flavor == gcfRepRapFirmware; - if (custom_gcode_sets_temperature(gcode, 104, 109, include_g10, temp_by_gcode)) { + if (! autoemit || custom_gcode_sets_temperature(gcode, 104, 109, include_g10, temp_by_gcode)) { // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code. int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); - if (temp_by_gcode >= 0 && temp_by_gcode < 1000) + if (autoemit && temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; m_writer.set_temperature(temp, wait, first_printing_extruder_id); } else { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 21b712217..a58492062 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -482,7 +482,7 @@ static std::vector s_Preset_machine_limits_options { }; static std::vector s_Preset_printer_options { - "printer_technology", + "printer_technology", "autoemit_temperature_commands", "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2afe02517..67407e591 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -58,6 +58,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n // Cache the plenty of parameters, which influence the G-code generator only, // or they are only notes not influencing the generated G-code. static std::unordered_set steps_gcode = { + "autoemit_temperature_commands", "avoid_crossing_perimeters", "avoid_crossing_perimeters_max_detour", "bed_shape", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 62f9a9a2f..8a8b70501 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2490,15 +2490,25 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionInt(-5)); + def = this->add("autoemit_temperature_commands", coBool); + def->label = L("Emit temperature commands automatically"); + def->tooltip = L("When enabled, PrusaSlicer will check whether your Custom Start G-Code contains M104 or M190. " + "If so, the temperatures will not be emitted automatically so you're free to customize " + "the order of heating commands and other custom actions. Note that you can use " + "placeholder variables for all PrusaSlicer settings, so you can put " + "a \"M109 S[first_layer_temperature]\" command wherever you want.\n" + "If your Custom Start G-Code does NOT contain M104 or M190, " + "PrusaSlicer will execute the Start G-Code after bed reached its target temperature " + "and extruder just started heating.\n\n" + "When disabled, PrusaSlicer will NOT emit commands to heat up extruder and bed, " + "leaving both to Custom Start G-Code."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("start_gcode", coString); def->label = L("Start G-code"); - def->tooltip = L("This start procedure is inserted at the beginning, after bed has reached " - "the target temperature and extruder just started heating, and before extruder " - "has finished heating. If PrusaSlicer detects M104 or M190 in your custom codes, " - "such commands will not be prepended automatically so you're free to customize " - "the order of heating commands and other custom actions. Note that you can use " - "placeholder variables for all PrusaSlicer settings, so you can put " - "a \"M109 S[first_layer_temperature]\" command wherever you want."); + def->tooltip = L("This start procedure is inserted at the beginning, possibly prepended by " + "temperature-changing commands. See 'autoemit_temperature_commands'."); def->multiline = true; def->full_width = true; def->height = 12; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 265628d78..d29afe5e7 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -661,6 +661,7 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( GCodeConfig, + ((ConfigOptionBool, autoemit_temperature_commands)) ((ConfigOptionString, before_layer_gcode)) ((ConfigOptionString, between_objects_gcode)) ((ConfigOptionFloats, deretract_speed)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6227fc537..22e23d84e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2411,6 +2411,9 @@ void TabPrinter::build_fff() option.opt.height = 3 * gcode_field_height;//150; optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Start G-Code options")); + optgroup->append_single_option_line("autoemit_temperature_commands"); + optgroup = page->new_optgroup(L("End G-code"), 0); optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); From 4b27210b6e5c248aa59883d222cc078207a77d5e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Mar 2023 22:03:01 +0100 Subject: [PATCH 155/201] Bypass a crash when validating custom gcodes --- src/slic3r/GUI/Tab.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 22e23d84e..51298f175 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4515,6 +4515,8 @@ bool Tab::validate_custom_gcodes() if (!opt_group->is_activated()) break; std::string key = opt_group->opt_map().begin()->first; + if (key == "autoemit_temperature_commands") + continue; valid &= validate_custom_gcode(opt_group->title, boost::any_cast(opt_group->get_value(key))); if (!valid) break; From dcfbff5ec8cc4ced4dabd90ddb46edf8c6a65b64 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Mar 2023 15:42:34 +0100 Subject: [PATCH 156/201] Check value of the parameter "How to apply limits", when Klipper is selected + Suppress silent mode for G-code flavor that is different from MarlinFirmware or MarlinLegacy --- src/slic3r/GUI/Tab.cpp | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 51298f175..3dbbcc260 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2377,14 +2377,44 @@ void TabPrinter::build_fff() } } if (opt_key == "gcode_flavor") { - const int flavor = boost::any_cast(value); - bool supports_travel_acceleration = (flavor == int(gcfMarlinFirmware) || flavor == int(gcfRepRapFirmware)); - bool supports_min_feedrates = (flavor == int(gcfMarlinFirmware) || flavor == int(gcfMarlinLegacy)); + const GCodeFlavor flavor = static_cast(boost::any_cast(value)); + bool supports_travel_acceleration = (flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware); + bool supports_min_feedrates = (flavor == gcfMarlinFirmware || flavor == gcfMarlinLegacy); if (supports_travel_acceleration != m_supports_travel_acceleration || supports_min_feedrates != m_supports_min_feedrates) { m_rebuild_kinematics_page = true; m_supports_travel_acceleration = supports_travel_acceleration; m_supports_min_feedrates = supports_min_feedrates; } + + const bool is_silent_mode = m_config->option("silent_mode")->getBool(); + const bool is_emit_to_gcode = m_config->option("machine_limits_usage")->getInt() == static_cast(MachineLimitsUsage::EmitToGCode); + if ((flavor == gcfKlipper && is_emit_to_gcode) || (!m_supports_min_feedrates && is_silent_mode)) { + DynamicPrintConfig new_conf = *m_config; + wxString msg; + + if (flavor == gcfKlipper && is_emit_to_gcode) { + msg = _L("Emitting machine limits to G-code is not supported with Klipper G-code flavor.\n" + "The option was switched to \"Use for time estimate\"."); + + auto machine_limits_usage = static_cast*>(m_config->option("machine_limits_usage")->clone()); + machine_limits_usage->value = MachineLimitsUsage::TimeEstimateOnly; + new_conf.set_key_value("machine_limits_usage", machine_limits_usage); + } + + if (!m_supports_min_feedrates && is_silent_mode) { + if (!msg.IsEmpty()) + msg += "\n\n"; + msg += _L("Stealth mode for machine limits to G-code is not supported with selected G-code flavor.\n" + "The Stealth mode was suppressed."); + + auto silent_mode = static_cast(m_config->option("silent_mode")->clone()); + silent_mode->value = false; + new_conf.set_key_value("silent_mode", silent_mode); + } + + InfoDialog(parent(), _L("G-code flavor is switched"), msg).ShowModal(); + load_config(new_conf); + } } build_unregular_pages(); update_dirty(); From 51129fd4f3317986add877d4c6bc36e32084567f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 23 Mar 2023 09:16:33 +0100 Subject: [PATCH 157/201] Follow-up https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/87d77f338de5206a3c6110cf3cda4ebab3f414e6 - Added control of a value of changed parameter "How to apply limits", when Klipper is selected --- src/slic3r/GUI/Tab.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3dbbcc260..9797e68cd 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2260,6 +2260,12 @@ void TabPrinter::build_print_host_upload_group(Page* page) optgroup->append_line(line); } +static wxString get_info_klipper_string() +{ + return _L("Emitting machine limits to G-code is not supported with Klipper G-code flavor.\n" + "The option was switched to \"Use for time estimate\"."); +} + void TabPrinter::build_fff() { if (!m_pages.empty()) @@ -2386,22 +2392,20 @@ void TabPrinter::build_fff() m_supports_min_feedrates = supports_min_feedrates; } - const bool is_silent_mode = m_config->option("silent_mode")->getBool(); const bool is_emit_to_gcode = m_config->option("machine_limits_usage")->getInt() == static_cast(MachineLimitsUsage::EmitToGCode); - if ((flavor == gcfKlipper && is_emit_to_gcode) || (!m_supports_min_feedrates && is_silent_mode)) { + if ((flavor == gcfKlipper && is_emit_to_gcode) || (!m_supports_min_feedrates && m_use_silent_mode)) { DynamicPrintConfig new_conf = *m_config; wxString msg; if (flavor == gcfKlipper && is_emit_to_gcode) { - msg = _L("Emitting machine limits to G-code is not supported with Klipper G-code flavor.\n" - "The option was switched to \"Use for time estimate\"."); + msg = get_info_klipper_string(); auto machine_limits_usage = static_cast*>(m_config->option("machine_limits_usage")->clone()); machine_limits_usage->value = MachineLimitsUsage::TimeEstimateOnly; new_conf.set_key_value("machine_limits_usage", machine_limits_usage); } - if (!m_supports_min_feedrates && is_silent_mode) { + if (!m_supports_min_feedrates && m_use_silent_mode) { if (!msg.IsEmpty()) msg += "\n\n"; msg += _L("Stealth mode for machine limits to G-code is not supported with selected G-code flavor.\n" @@ -2663,6 +2667,27 @@ PageShp TabPrinter::build_kinematics_page() optgroup->append_line(line); } + optgroup->m_on_change = [this](const t_config_option_key& opt_key, boost::any value) + { + if (opt_key == "machine_limits_usage" && + static_cast(boost::any_cast(value)) == MachineLimitsUsage::EmitToGCode && + static_cast(m_config->option("gcode_flavor")->getInt()) == gcfKlipper) + { + DynamicPrintConfig new_conf = *m_config; + + auto machine_limits_usage = static_cast*>(m_config->option("machine_limits_usage")->clone()); + machine_limits_usage->value = MachineLimitsUsage::TimeEstimateOnly; + + new_conf.set_key_value("machine_limits_usage", machine_limits_usage); + + InfoDialog(parent(), wxEmptyString, get_info_klipper_string()).ShowModal(); + load_config(new_conf); + } + + update_dirty(); + update(); + }; + if (m_use_silent_mode) { // Legend for OptionsGroups auto optgroup = page->new_optgroup(""); From 847a34c64420688a06cb72465aff120153b84290 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 23 Mar 2023 15:21:38 +0100 Subject: [PATCH 158/201] Do not clamp travel acceleration when current firmware flavor does not allow separate setting of print and travel acceleration --- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode/GCodeProcessor.cpp | 6 ++++++ src/libslic3r/GCodeWriter.cpp | 14 +++++++++----- src/libslic3r/GCodeWriter.hpp | 4 +++- src/slic3r/GUI/Tab.cpp | 3 ++- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fcfd83da0..3113560f4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3075,7 +3075,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string for (size_t i = 1; i < travel.size(); ++ i) gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment); - if (! m_writer.supports_PT()) { + if (! GCodeWriter::supports_separate_travel_acceleration(config().gcode_flavor)) { // In case that this flavor does not support separate print and travel acceleration, // reset acceleration to default. gcode += m_writer.set_travel_acceleration((unsigned int)(m_config.travel_acceleration.value + 0.5)); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ead792662..a2c867505 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3,6 +3,7 @@ #include "libslic3r/Print.hpp" #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/format.hpp" +#include "libslic3r/GCodeWriter.hpp" #include "GCodeProcessor.hpp" #include @@ -610,7 +611,12 @@ for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode:: float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i); m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration; m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION; + float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i); + if ( ! GCodeWriter::supports_separate_travel_acceleration(config.gcode_flavor.value) || config.machine_limits_usage.value != MachineLimitsUsage::EmitToGCode) { + // Only clamp travel acceleration when it is accessible in machine limits. + max_travel_acceleration = 0; + } m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration; m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION; } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 45176baaa..5080fabb4 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -15,9 +15,10 @@ namespace Slic3r { -bool GCodeWriter::supports_PT() const +// static +bool GCodeWriter::supports_separate_travel_acceleration(GCodeFlavor flavor) { - return (FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfMarlinFirmware) || FLAVOR_IS(gcfRepRapFirmware)); + return (flavor == gcfRepetier || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware); } void GCodeWriter::apply_print_config(const PrintConfig &print_config) @@ -30,6 +31,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) || print_config.gcode_flavor.value == gcfRepRapFirmware; m_max_acceleration = static_cast(std::round((use_mach_limits && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ? print_config.machine_max_acceleration_extruding.values.front() : 0)); + m_max_travel_acceleration = static_cast(std::round((use_mach_limits && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode && supports_separate_travel_acceleration(print_config.gcode_flavor.value)) ? + print_config.machine_max_acceleration_travel.values.front() : 0)); } void GCodeWriter::set_extruders(std::vector extruder_ids) @@ -163,12 +166,13 @@ std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait std::string GCodeWriter::set_acceleration_internal(Acceleration type, unsigned int acceleration) { // Clamp the acceleration to the allowed maximum. - // TODO: What about max travel acceleration ? Currently it is clamped by the extruding acceleration !!! - if (m_max_acceleration > 0 && acceleration > m_max_acceleration) + if (type == Acceleration::Print && m_max_acceleration > 0 && acceleration > m_max_acceleration) acceleration = m_max_acceleration; + if (type == Acceleration::Travel && m_max_travel_acceleration > 0 && acceleration > m_max_travel_acceleration) + acceleration = m_max_travel_acceleration; // Are we setting travel acceleration for a flavour that supports separate travel and print acc? - bool separate_travel = (type == Acceleration::Travel && supports_PT()); + bool separate_travel = (type == Acceleration::Travel && supports_separate_travel_acceleration(this->config.gcode_flavor)); auto& last_value = separate_travel ? m_last_travel_acceleration : m_last_acceleration ; if (acceleration == 0 || acceleration == last_value) diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 2a2a488fa..9e5fce702 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -71,7 +71,7 @@ public: Vec3d get_position() const { return m_pos; } // Returns whether this flavor supports separate print and travel acceleration. - bool supports_PT() const; + static bool supports_separate_travel_acceleration(GCodeFlavor flavor); // To be called by the CoolingBuffer from another thread. static std::string set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed); @@ -90,6 +90,8 @@ private: // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware. // If set to zero, the limit is not in action. unsigned int m_max_acceleration; + unsigned int m_max_travel_acceleration; + unsigned int m_last_bed_temperature; bool m_last_bed_temperature_reached; double m_lifted; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9797e68cd..b095d4ea2 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -7,6 +7,7 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" +#include "libslic3r/GCodeWriter.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/PrintHost.hpp" @@ -2384,7 +2385,7 @@ void TabPrinter::build_fff() } if (opt_key == "gcode_flavor") { const GCodeFlavor flavor = static_cast(boost::any_cast(value)); - bool supports_travel_acceleration = (flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware); + bool supports_travel_acceleration = GCodeWriter::supports_separate_travel_acceleration(flavor); bool supports_min_feedrates = (flavor == gcfMarlinFirmware || flavor == gcfMarlinLegacy); if (supports_travel_acceleration != m_supports_travel_acceleration || supports_min_feedrates != m_supports_min_feedrates) { m_rebuild_kinematics_page = true; From 0a52ef8da7ea84a30c9615f60938e1332e4da4a4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 24 Mar 2023 09:22:50 +0100 Subject: [PATCH 159/201] Fix format of M204 emitted from machine limits for various fws --- src/libslic3r/GCode.cpp | 26 ++++++++++++++------------ src/libslic3r/PrintConfig.cpp | 9 ++++----- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3113560f4..041194435 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1663,23 +1663,25 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) int(print.config().machine_max_feedrate_e.values.front() * factor + 0.5), factor == 60 ? "mm / min" : "mm / sec"); - // Now M204 - acceleration. This one is quite hairy thanks to how Marlin guys care about - // backwards compatibility: https://github.com/prusa3d/PrusaSlicer/issues/1089 - // Legacy Marlin should export travel acceleration the same as printing acceleration. - // MarlinFirmware has the two separated. - int travel_acc = flavor == gcfMarlinLegacy - ? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5) - : int(print.config().machine_max_acceleration_travel.values.front() + 0.5); - // Retract acceleration not accepted in M204 in RRF + // Now M204 - acceleration. This one is quite hairy... if (flavor == gcfRepRapFirmware) + // Uses M204 P[print] T[travel] file.write_format("M204 P%d T%d ; sets acceleration (P, T), mm/sec^2\n", int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), - travel_acc); - else - file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", + int(print.config().machine_max_acceleration_travel.values.front() + 0.5)); + else if (flavor == gcfMarlinLegacy) + // Legacy Marlin uses M204 S[print] T[retract] + file.write_format("M204 " + "S%d T%d ; sets acceleration (S) and retract acceleration (R), mm/sec^2\n", + int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), + int(print.config().machine_max_acceleration_retracting.values.front() + 0.5)); + else if (flavor == gcfMarlinFirmware) + // New Marlin uses M204 P[print] R[retract] T[travel] + file.write_format("M204 " + "P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_retracting.values.front() + 0.5), - travel_acc); + int(print.config().machine_max_acceleration_travel.values.front() + 0.5)); + else + assert(false); assert(is_decimal_separator_point()); file.write_format(flavor == gcfRepRapFirmware diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8a8b70501..0089ce457 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1803,9 +1803,7 @@ void PrintConfigDef::init_fff_params() def = this->add("machine_max_acceleration_extruding", coFloats); def->full_label = L("Maximum acceleration when extruding"); def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration when extruding (M204 P)\n\n" - "Marlin (legacy) firmware flavor will use this also " - "as travel acceleration (M204 T)."); + def->tooltip = L("Maximum acceleration when extruding"); def->sidetext = L("mm/s²"); def->min = 0; def->mode = comAdvanced; @@ -1816,7 +1814,8 @@ void PrintConfigDef::init_fff_params() def = this->add("machine_max_acceleration_retracting", coFloats); def->full_label = L("Maximum acceleration when retracting"); def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration when retracting (M204 R)"); + def->tooltip = L("Maximum acceleration when retracting.\n\n" + "Not used for RepRapFirmware, which does not support it."); def->sidetext = L("mm/s²"); def->min = 0; def->mode = comAdvanced; @@ -1826,7 +1825,7 @@ void PrintConfigDef::init_fff_params() def = this->add("machine_max_acceleration_travel", coFloats); def->full_label = L("Maximum acceleration for travel moves"); def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration for travel moves (M204 T)"); + def->tooltip = L("Maximum acceleration for travel moves."); def->sidetext = L("mm/s²"); def->min = 0; def->mode = comAdvanced; From c9b15736dac81a2b296ac3501214c726c2b214af Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 24 Mar 2023 09:56:40 +0100 Subject: [PATCH 160/201] Fixed compilation errors and warnings --- src/libslic3r/GCode.cpp | 4 ++-- src/libslic3r/I18N.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 041194435..d4278ed74 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1671,12 +1671,12 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) int(print.config().machine_max_acceleration_travel.values.front() + 0.5)); else if (flavor == gcfMarlinLegacy) // Legacy Marlin uses M204 S[print] T[retract] - file.write_format("M204 " + "S%d T%d ; sets acceleration (S) and retract acceleration (R), mm/sec^2\n", + file.write_format("M204 S%d T%d ; sets acceleration (S) and retract acceleration (R), mm/sec^2\n", int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_retracting.values.front() + 0.5)); else if (flavor == gcfMarlinFirmware) // New Marlin uses M204 P[print] R[retract] T[travel] - file.write_format("M204 " + "P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", + file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_retracting.values.front() + 0.5), int(print.config().machine_max_acceleration_travel.values.front() + 0.5)); diff --git a/src/libslic3r/I18N.hpp b/src/libslic3r/I18N.hpp index 3cc196b6a..5d2068a3d 100644 --- a/src/libslic3r/I18N.hpp +++ b/src/libslic3r/I18N.hpp @@ -28,9 +28,9 @@ namespace I18N { #error L macro is defined where it shouldn't be. Didn't you include slic3r/GUI/I18N.hpp in libslic3r by mistake? #endif namespace { - const char* L(const char* s) { return s; } - const char* L_CONTEXT(const char* s, const char* context) { return s; } - std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } + [[maybe_unused]] const char* L(const char* s) { return s; } + [[maybe_unused]] const char* L_CONTEXT(const char* s, const char* context) { return s; } + [[maybe_unused]] std::string _u8L(const char* s) { return Slic3r::I18N::translate(s); } } #endif From a391d4c445a8e5505eeecdfdad1e9bb125905335 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 24 Mar 2023 10:07:25 +0100 Subject: [PATCH 161/201] Added missed includes of "format.hpp" --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 1 + src/slic3r/GUI/GUI_ObjectSettings.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 7d9a87b58..02ef719f1 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1,5 +1,6 @@ #include "GUI_ObjectManipulation.hpp" #include "I18N.hpp" +#include "format.hpp" #include "BitmapComboBox.hpp" #include "GLCanvas3D.hpp" diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 41cad8792..a6d85cef7 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -12,6 +12,7 @@ #include #include "I18N.hpp" +#include "format.hpp" #include "ConfigManipulation.hpp" #include From 7f1df7b110c89864b1a10bc52055e6f194645d24 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 24 Mar 2023 12:57:07 +0100 Subject: [PATCH 162/201] Fixed Fit to print volume command when applied to an object sliced using SLA printer --- src/slic3r/GUI/GLCanvas3D.cpp | 15 ++++++++++++--- src/slic3r/GUI/Selection.cpp | 14 +++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dc4acebbc..b5b4a9ca0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3605,6 +3605,9 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) int instance_idx = v->instance_idx(); int volume_idx = v->volume_idx(); + if (volume_idx < 0) + continue; + std::pair done_id(object_idx, instance_idx); if (0 <= object_idx && object_idx < (int)m_model->objects.size()) { @@ -3619,7 +3622,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) #else model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); #endif // ENABLE_WORLD_COORDINATE - else if (volume_idx >= 0 && selection_mode == Selection::Volume) + else if (selection_mode == Selection::Volume) #if ENABLE_WORLD_COORDINATE model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); #else @@ -3710,6 +3713,9 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) const int instance_idx = v->instance_idx(); const int volume_idx = v->volume_idx(); + if (volume_idx < 0) + continue; + done.insert(std::pair(object_idx, instance_idx)); // Rotate instances/volumes. @@ -3723,7 +3729,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); #endif // ENABLE_WORLD_COORDINATE } - else if (selection_mode == Selection::Volume && volume_idx >= 0) { + else if (selection_mode == Selection::Volume) { #if ENABLE_WORLD_COORDINATE model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); #else @@ -3786,6 +3792,9 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) const int instance_idx = v->instance_idx(); const int volume_idx = v->volume_idx(); + if (volume_idx < 0) + continue; + done.insert(std::pair(object_idx, instance_idx)); // Rotate instances/volumes @@ -3799,7 +3808,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); #endif // ENABLE_WORLD_COORDINATE } - else if (selection_mode == Selection::Volume && volume_idx >= 0) { + else if (selection_mode == Selection::Volume) { #if ENABLE_WORLD_COORDINATE model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2b77fe7ff..28cf367fd 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1396,6 +1396,18 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume) // used to keep track whether the undo/redo snapshot has already been taken bool undoredo_snapshot = false; + if (wxGetApp().plater()->printer_technology() == ptSLA) { + // remove SLA auxiliary volumes from the selection to ensure that the proper bounding box is calculated + std::vector to_remove; + for (unsigned int i : m_list) { + if ((*m_volumes)[i]->volume_idx() < 0) + to_remove.push_back(i); + } + + if (!to_remove.empty()) + remove_volumes(m_mode, to_remove); + } + switch (volume.type()) { case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; } @@ -3006,7 +3018,7 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV continue; const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear(); for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i) - if (volumes[i]->object_idx() == idx_object) { + if (volumes[i]->object_idx() == idx_object && volumes[i]->volume_idx() >= 0) { const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear(); assert(is_rotation_xy_synchronized(rotation, rotation0)); } From 5b115b79723fb7bf16f303df8bef6d8c6fa766d2 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 24 Mar 2023 13:55:48 +0100 Subject: [PATCH 163/201] PlaceholderParser: Implemented skipping of inactive if / else / endif and ternary operator branches, thus missing variables or addressing outside of the variable range in an inactive branch will not trigger an error. --- src/libslic3r/PlaceholderParser.cpp | 517 +++++++++++++------- tests/libslic3r/test_placeholder_parser.cpp | 32 +- 2 files changed, 370 insertions(+), 179 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 94a1259b6..06a4d3960 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -177,6 +177,7 @@ namespace client int index { -1 }; boost::iterator_range it_range; + bool empty() const { return opt == nullptr; } bool has_index() const { return index != -1; } }; @@ -292,6 +293,9 @@ namespace client { std::string out; switch (this->type()) { + case TYPE_EMPTY: + // Inside an if / else block to be skipped. + break; case TYPE_BOOL: out = this->b() ? "true" : "false"; break; case TYPE_INT: out = std::to_string(this->i()); break; case TYPE_DOUBLE: @@ -321,6 +325,9 @@ namespace client expr unary_minus(const Iterator start_pos) const { switch (this->type()) { + case TYPE_EMPTY: + // Inside an if / else block to be skipped. + return expr(); case TYPE_INT : return expr(- this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: @@ -336,6 +343,9 @@ namespace client expr unary_integer(const Iterator start_pos) const { switch (this->type()) { + case TYPE_EMPTY: + // Inside an if / else block to be skipped. + return expr(); case TYPE_INT: return expr(this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: @@ -351,6 +361,9 @@ namespace client expr round(const Iterator start_pos) const { switch (this->type()) { + case TYPE_EMPTY: + // Inside an if / else block to be skipped. + return expr(); case TYPE_INT: return expr(this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: @@ -366,6 +379,9 @@ namespace client expr unary_not(const Iterator start_pos) const { switch (this->type()) { + case TYPE_EMPTY: + // Inside an if / else block to be skipped. + return expr(); case TYPE_BOOL: return expr(! this->b(), start_pos, this->it_range.end()); default: @@ -378,7 +394,9 @@ namespace client expr &operator+=(const expr &rhs) { - if (this->type() == TYPE_STRING) { + if (this->type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped. + } else if (this->type() == TYPE_STRING) { // Convert the right hand side to string and append. *m_data.s += rhs.to_string(); } else if (rhs.type() == TYPE_STRING) { @@ -399,72 +417,98 @@ namespace client expr &operator-=(const expr &rhs) { - const char *err_msg = "Cannot subtract non-numeric types."; - this->throw_if_not_numeric(err_msg); - rhs.throw_if_not_numeric(err_msg); - if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) - this->set_d_lite(this->as_d() - rhs.as_d()); - else - m_data.i -= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + if (this->type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped. + this->reset(); + } else { + const char *err_msg = "Cannot subtract non-numeric types."; + this->throw_if_not_numeric(err_msg); + rhs.throw_if_not_numeric(err_msg); + if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) + this->set_d_lite(this->as_d() - rhs.as_d()); + else + m_data.i -= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + } return *this; } expr &operator*=(const expr &rhs) { - const char *err_msg = "Cannot multiply with non-numeric type."; - this->throw_if_not_numeric(err_msg); - rhs.throw_if_not_numeric(err_msg); - if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) - this->set_d_lite(this->as_d() * rhs.as_d()); - else - m_data.i *= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + if (this->type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped. + this->reset(); + } else { + const char *err_msg = "Cannot multiply with non-numeric type."; + this->throw_if_not_numeric(err_msg); + rhs.throw_if_not_numeric(err_msg); + if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) + this->set_d_lite(this->as_d() * rhs.as_d()); + else + m_data.i *= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + } return *this; } expr &operator/=(const expr &rhs) { - this->throw_if_not_numeric("Cannot divide a non-numeric type."); - rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); - if (rhs.type() == TYPE_INT ? (rhs.i() == 0) : (rhs.d() == 0.)) - rhs.throw_exception("Division by zero"); - if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) - this->set_d_lite(this->as_d() / rhs.as_d()); - else - m_data.i /= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + if (this->type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped. + this->reset(); + } else { + this->throw_if_not_numeric("Cannot divide a non-numeric type."); + rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); + if (rhs.type() == TYPE_INT ? (rhs.i() == 0) : (rhs.d() == 0.)) + rhs.throw_exception("Division by zero"); + if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) + this->set_d_lite(this->as_d() / rhs.as_d()); + else + m_data.i /= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + } return *this; } expr &operator%=(const expr &rhs) { - this->throw_if_not_numeric("Cannot divide a non-numeric type."); - rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); - if (rhs.type() == TYPE_INT ? (rhs.i() == 0) : (rhs.d() == 0.)) - rhs.throw_exception("Division by zero"); - if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) - this->set_d_lite(std::fmod(this->as_d(), rhs.as_d())); - else - m_data.i %= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + if (this->type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped. + this->reset(); + } else { + this->throw_if_not_numeric("Cannot divide a non-numeric type."); + rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); + if (rhs.type() == TYPE_INT ? (rhs.i() == 0) : (rhs.d() == 0.)) + rhs.throw_exception("Division by zero"); + if (this->type() == TYPE_DOUBLE || rhs.type() == TYPE_DOUBLE) + this->set_d_lite(std::fmod(this->as_d(), rhs.as_d())); + else + m_data.i %= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + } return *this; } static void to_string2(expr &self, std::string &out) { - out = self.to_string(); + if (self.type() != TYPE_EMPTY) + // Not inside an if / else block to be skipped + out = self.to_string(); } static void evaluate_boolean(expr &self, bool &out) { - if (self.type() != TYPE_BOOL) - self.throw_exception("Not a boolean expression"); - out = self.b(); + if (self.type() != TYPE_EMPTY) { + // Not inside an if / else block to be skipped + if (self.type() != TYPE_BOOL) + self.throw_exception("Not a boolean expression"); + out = self.b(); + } } static void evaluate_boolean_to_string(expr &self, std::string &out) { + assert(self.type() != TYPE_EMPTY); if (self.type() != TYPE_BOOL) self.throw_exception("Not a boolean expression"); out = self.b() ? "true" : "false"; @@ -473,6 +517,9 @@ namespace client // Is lhs==rhs? Store the result into lhs. static void compare_op(expr &lhs, expr &rhs, char op, bool invert) { + if (lhs.type() == TYPE_EMPTY) + // Inside an if / else block to be skipped + return; bool value = false; if (lhs.numeric_type() && rhs.numeric_type()) { // Both types are numeric. @@ -529,6 +576,9 @@ namespace client // Store the result into param1. static void function_2params(expr ¶m1, expr ¶m2, Function2ParamsType fun) { + if (param1.type() == TYPE_EMPTY) + // Inside an if / else block to be skipped + return; throw_if_not_numeric(param1); throw_if_not_numeric(param2); if (param1.type() == TYPE_DOUBLE || param2.type() == TYPE_DOUBLE) { @@ -556,6 +606,9 @@ namespace client // Store the result into param1. static void random(expr ¶m1, expr ¶m2, std::mt19937 &rng) { + if (param1.type() == TYPE_EMPTY) + // Inside an if / else block to be skipped + return; throw_if_not_numeric(param1); throw_if_not_numeric(param2); if (param1.type() == TYPE_DOUBLE || param2.type() == TYPE_DOUBLE) @@ -569,6 +622,9 @@ namespace client template static void digits(expr ¶m1, expr ¶m2, expr ¶m3) { + if (param1.type() == TYPE_EMPTY) + // Inside an if / else block to be skipped + return; throw_if_not_numeric(param1); if (param2.type() != TYPE_INT) param2.throw_exception("digits: second parameter must be integer"); @@ -590,6 +646,9 @@ namespace client static void regex_op(const expr &lhs, boost::iterator_range &rhs, char op, expr &out) { + if (lhs.type() == TYPE_EMPTY) + // Inside an if / else block to be skipped + return; const std::string *subject = nullptr; if (lhs.type() == TYPE_STRING) { // One type is string, the other could be converted to string. @@ -618,6 +677,11 @@ namespace client } template static void one_of_test(const expr &match, const expr &pattern, expr &out) { + if (match.type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped + out.reset(); + return; + } if (! out.b()) { if (match.type() != TYPE_STRING) match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); @@ -635,6 +699,11 @@ namespace client } } static void one_of_test_regex(const expr &match, boost::iterator_range &pattern, expr &out) { + if (match.type() == TYPE_EMPTY) { + // Inside an if / else block to be skipped + out.reset(); + return; + } if (! out.b()) { if (match.type() != TYPE_STRING) match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); @@ -644,6 +713,9 @@ namespace client static void logical_op(expr &lhs, expr &rhs, char op) { + if (lhs.type() == TYPE_EMPTY) + // Inside an if / else block to be skipped + return; bool value = false; if (lhs.type() == TYPE_BOOL && rhs.type() == TYPE_BOOL) { value = (op == '|') ? (lhs.b() || rhs.b()) : (lhs.b() && rhs.b()); @@ -656,24 +728,6 @@ namespace client static void logical_or (expr &lhs, expr &rhs) { logical_op(lhs, rhs, '|'); } static void logical_and(expr &lhs, expr &rhs) { logical_op(lhs, rhs, '&'); } - static void ternary_op(expr &lhs, expr &rhs1, expr &rhs2) - { - if (lhs.type() != TYPE_BOOL) - lhs.throw_exception("Not a boolean expression"); - if (lhs.b()) - lhs = std::move(rhs1); - else - lhs = std::move(rhs2); - } - - static void set_if(bool &cond, bool ¬_yet_consumed, std::string &str_in, std::string &str_out) - { - if (cond && not_yet_consumed) { - str_out = str_in; - not_yet_consumed = false; - } - } - void throw_exception(const char *message) const { boost::throw_exception(qi::expectation_failure( @@ -745,6 +799,33 @@ namespace client // Should the parser consider the parsed string to be a macro or a boolean expression? static bool evaluate_full_macro(const MyContext *ctx) { return ! ctx->just_boolean_expression; } + // Entering a conditional block. + static void block_enter(const MyContext *ctx, const bool condition) + { + if (ctx->skipping() || ! condition) + ++ ctx->m_depth_suppressed; + } + // Exiting a conditional block. + static void block_exit(const MyContext *ctx, const bool condition, bool ¬_yet_consumed, std::string &data_in, std::string &data_out) + { + if (ctx->skipping()) + -- ctx->m_depth_suppressed; + else if (condition && not_yet_consumed) { + data_out = std::move(data_in); + not_yet_consumed = false; + } + } + template + static void block_exit_ternary(const MyContext* ctx, const bool condition, DataType &data_in, DataType &data_out) + { + if (ctx->skipping()) + -- ctx->m_depth_suppressed; + else if (condition) + data_out = std::move(data_in); + } + // Inside a block, which is conditionally suppressed? + bool skipping() const { return m_depth_suppressed > 0; } + const ConfigOption* optptr(const t_config_option_key &opt_key) const override { const ConfigOption *opt = nullptr; @@ -758,7 +839,7 @@ namespace client } const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } - ConfigOption* resolve_output_symbol(const std::string &opt_key) const { + ConfigOption* resolve_output_symbol(const std::string &opt_key) const { ConfigOption *out = nullptr; if (this->config_outputs) out = this->config_outputs->optptr(opt_key, false); @@ -783,6 +864,9 @@ namespace client boost::iterator_range &opt_key, std::string &output) { + if (ctx->skipping()) + return; + std::string opt_key_str(opt_key.begin(), opt_key.end()); const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); size_t idx = ctx->current_extruder_id; @@ -820,6 +904,9 @@ namespace client boost::iterator_range &opt_vector_index, std::string &output) { + if (ctx->skipping()) + return; + std::string opt_key_str(opt_key.begin(), opt_key.end()); const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); if (opt == nullptr) { @@ -850,15 +937,17 @@ namespace client boost::iterator_range &opt_key, OptWithPos &output) { - const std::string key{ opt_key.begin(), opt_key.end() }; - const ConfigOption *opt = ctx->resolve_symbol(key); - if (opt == nullptr) { - opt = ctx->resolve_output_symbol(key); - if (opt == nullptr) - ctx->throw_exception("Not a variable name", opt_key); - output.writable = true; + if (! ctx->skipping()) { + const std::string key{ opt_key.begin(), opt_key.end() }; + const ConfigOption *opt = ctx->resolve_symbol(key); + if (opt == nullptr) { + opt = ctx->resolve_output_symbol(key); + if (opt == nullptr) + ctx->throw_exception("Not a variable name", opt_key); + output.writable = true; + } + output.opt = opt; } - output.opt = opt; output.it_range = opt_key; } @@ -870,12 +959,15 @@ namespace client Iterator it_end, OptWithPos &output) { - if (! opt.opt->is_vector()) - ctx->throw_exception("Cannot index a scalar variable", opt.it_range); - if (index < 0) - ctx->throw_exception("Referencing a vector variable with a negative index", opt.it_range); - output = opt; - output.index = index; + if (! ctx->skipping()) { + if (! opt.opt->is_vector()) + ctx->throw_exception("Cannot index a scalar variable", opt.it_range); + if (index < 0) + ctx->throw_exception("Referencing a vector variable with a negative index", opt.it_range); + output = opt; + output.index = index; + } else + output = opt; output.it_range.end() = it_end; } @@ -887,6 +979,9 @@ namespace client OptWithPos &opt, expr &output) { + if (ctx->skipping()) + return; + assert(opt.opt->is_scalar()); switch (opt.opt->type()) { @@ -948,6 +1043,9 @@ namespace client OptWithPos &opt, expr &output) { + if (ctx->skipping()) + return; + assert(opt.opt->is_vector()); if (! opt.has_index()) ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); @@ -1102,10 +1200,12 @@ namespace client OptWithPos &opt, expr &output) { - if (opt.opt->is_vector()) - vector_element_to_expr(ctx, opt, output); - else - scalar_variable_to_expr(ctx, opt, output); + if (! ctx->skipping()) { + if (opt.opt->is_vector()) + vector_element_to_expr(ctx, opt, output); + else + scalar_variable_to_expr(ctx, opt, output); + } output.it_range = opt.it_range; } @@ -1117,7 +1217,8 @@ namespace client OptWithPos &opt, expr &output) { - if (opt.opt->is_vector()) { + if (ctx->skipping()) { + } else if (opt.opt->is_vector()) { if (! opt.has_index()) ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); const ConfigOptionVectorBase *vec = static_cast(opt.opt); @@ -1145,26 +1246,26 @@ namespace client const boost::iterator_range &it_range, NewOldVariable &out) { - t_config_option_key key(std::string(it_range.begin(), it_range.end())); - if (const ConfigOption* opt = ctx->resolve_symbol(key); opt) - ctx->throw_exception("Symbol is already defined in read-only system dictionary", it_range); - if (ctx->config_outputs && ctx->config_outputs->optptr(key)) - ctx->throw_exception("Symbol is already defined as system output variable", it_range); - - bool has_global_dictionary = ctx->context_data != nullptr && ctx->context_data->global_config; - if (global_variable) { - if (! has_global_dictionary) - ctx->throw_exception("Global variables are not available in this context", it_range); - if (ctx->config_local.optptr(key)) - ctx->throw_exception("Variable name already defined in local scope", it_range); - out.opt = ctx->context_data->global_config->optptr(key); - } else { - if (has_global_dictionary && ctx->context_data->global_config->optptr(key)) - ctx->throw_exception("Variable name already defined in global scope", it_range); - out.opt = ctx->config_local.optptr(key); + if (! ctx->skipping()) { + t_config_option_key key(std::string(it_range.begin(), it_range.end())); + if (const ConfigOption* opt = ctx->resolve_symbol(key); opt) + ctx->throw_exception("Symbol is already defined in read-only system dictionary", it_range); + if (ctx->config_outputs && ctx->config_outputs->optptr(key)) + ctx->throw_exception("Symbol is already defined as system output variable", it_range); + bool has_global_dictionary = ctx->context_data != nullptr && ctx->context_data->global_config; + if (global_variable) { + if (! has_global_dictionary) + ctx->throw_exception("Global variables are not available in this context", it_range); + if (ctx->config_local.optptr(key)) + ctx->throw_exception("Variable name already defined in local scope", it_range); + out.opt = ctx->context_data->global_config->optptr(key); + } else { + if (has_global_dictionary && ctx->context_data->global_config->optptr(key)) + ctx->throw_exception("Variable name already defined in global scope", it_range); + out.opt = ctx->config_local.optptr(key); + } + out.name = std::move(key); } - - out.name = std::move(key); out.it_range = it_range; } @@ -1175,11 +1276,13 @@ namespace client OptWithPos &opt, const expr ¶m) { - check_writable(ctx, opt); - if (opt.opt->is_vector()) - vector_variable_element_assign_scalar(ctx, opt, param); - else - scalar_variable_assign_scalar(ctx, opt, param); + if (! ctx->skipping()) { + check_writable(ctx, opt); + if (opt.opt->is_vector()) + vector_variable_element_assign_scalar(ctx, opt, param); + else + scalar_variable_assign_scalar(ctx, opt, param); + } } template @@ -1189,7 +1292,8 @@ namespace client NewOldVariable &lhs, const expr &rhs) { - if (lhs.opt) { + if (ctx->skipping()) { + } else if (lhs.opt) { if (lhs.opt->is_vector()) rhs.throw_exception("Cannot assign a scalar value to a vector variable."); OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; @@ -1215,7 +1319,8 @@ namespace client const expr &rhs_count, const expr &rhs_value) { - if (lhs.opt) { + if (ctx->skipping()) { + } else if (lhs.opt) { if (lhs.opt->is_scalar()) rhs_value.throw_exception("Cannot assign a vector value to a scalar variable."); OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; @@ -1241,10 +1346,12 @@ namespace client const expr &rhs_count, const expr &rhs_value) { - check_writable(ctx, lhs); - if (lhs.opt->is_scalar()) - rhs_value.throw_exception("Cannot assign a vector value to a scalar variable."); - vector_variable_assign_expr_with_count(ctx, lhs, rhs_count, rhs_value); + if (! ctx->skipping()) { + check_writable(ctx, lhs); + if (lhs.opt->is_scalar()) + rhs_value.throw_exception("Cannot assign a vector value to a scalar variable."); + vector_variable_assign_expr_with_count(ctx, lhs, rhs_count, rhs_value); + } } template @@ -1262,6 +1369,9 @@ namespace client OptWithPos &lhs, const std::vector> &il) { + if (ctx->skipping()) + return; + check_writable(ctx, lhs); if (lhs.opt->is_scalar()) { @@ -1311,6 +1421,9 @@ namespace client NewOldVariable &lhs, const std::vector> &il) { + if (ctx->skipping()) + return; + if (lhs.opt) { // Assign to an existing vector variable. OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; @@ -1355,7 +1468,7 @@ namespace client template static bool is_vector_variable_reference(const OptWithPos &var) { - return ! var.has_index() && var.opt->is_vector(); + return ! var.empty() && ! var.has_index() && var.opt->is_vector(); } // Called when checking whether the NewOldVariable could be assigned a vectir right hand side. @@ -1370,6 +1483,9 @@ namespace client OptWithPos &lhs, const OptWithPos &rhs) { + if (ctx->skipping()) + return; + check_writable(ctx, lhs); assert(lhs.opt->is_vector()); if (rhs.has_index() || ! rhs.opt->is_vector()) @@ -1399,6 +1515,10 @@ namespace client NewOldVariable &lhs, const OptWithPos &rhs) { + if (ctx->skipping()) + // Skipping, continue parsing. + return true; + if (lhs.opt) { assert(lhs.opt->is_vector()); OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true }; @@ -1422,9 +1542,11 @@ namespace client } template - static void initializer_list_append(std::vector> &list, expr &expr) + static void initializer_list_append(std::vector> &list, expr ¶m) { - list.emplace_back(std::move(expr)); + if (param.type() != expr::TYPE_EMPTY) + // not skipping + list.emplace_back(std::move(param)); } template @@ -1433,9 +1555,11 @@ namespace client OptWithPos &opt, expr &out) { - if (opt.has_index() || ! opt.opt->is_vector()) - ctx->throw_exception("parameter of empty() is not a vector variable", opt.it_range); - out.set_b(static_cast(opt.opt)->size() == 0); + if (! ctx->skipping()) { + if (opt.has_index() || ! opt.opt->is_vector()) + ctx->throw_exception("parameter of empty() is not a vector variable", opt.it_range); + out.set_b(static_cast(opt.opt)->size() == 0); + } out.it_range = opt.it_range; } @@ -1445,9 +1569,11 @@ namespace client OptWithPos &opt, expr &out) { - if (opt.has_index() || ! opt.opt->is_vector()) - ctx->throw_exception("parameter of size() is not a vector variable", opt.it_range); - out.set_i(int(static_cast(opt.opt)->size())); + if (! ctx->skipping()) { + if (opt.has_index() || ! opt.opt->is_vector()) + ctx->throw_exception("parameter of size() is not a vector variable", opt.it_range); + out.set_i(int(static_cast(opt.opt)->size())); + } out.it_range = opt.it_range; } @@ -1456,14 +1582,19 @@ namespace client template static void evaluate_index(expr &expr_index, int &output) { - if (expr_index.type() != expr::TYPE_INT) - expr_index.throw_exception("Non-integer index is not allowed to address a vector variable."); - output = expr_index.i(); + if (expr_index.type() != expr::TYPE_EMPTY) { + if (expr_index.type() != expr::TYPE_INT) + expr_index.throw_exception("Non-integer index is not allowed to address a vector variable."); + output = expr_index.i(); + } } template static void random(const MyContext *ctx, expr ¶m1, expr ¶m2) { + if (ctx->skipping()) + return; + if (ctx->context_data == nullptr) ctx->throw_exception("Random number generator not available in this context.", boost::iterator_range(param1.it_range.begin(), param2.it_range.end())); @@ -1525,6 +1656,10 @@ namespace client msg += ' '; msg += "^\n"; } + + private: + // For skipping execution of inactive conditional branches. + mutable int m_depth_suppressed{ 0 }; }; template @@ -1537,17 +1672,24 @@ namespace client std::vector table; static void init(const expr &x) { - if (!x.numeric_type()) - x.throw_exception("Interpolation value must be a number."); + if (x.type() != expr::TYPE_EMPTY) { + if (!x.numeric_type()) + x.throw_exception("Interpolation value must be a number."); + } } static void add_pair(const expr &x, const expr &y, InterpolateTableContext &table) { - if (! x.numeric_type()) - x.throw_exception("X value of a table point must be a number."); - if (! y.numeric_type()) - y.throw_exception("Y value of a table point must be a number."); - table.table.push_back({ x.as_d(), x.it_range, y.as_d() }); + if (x.type() != expr::TYPE_EMPTY) { + if (! x.numeric_type()) + x.throw_exception("X value of a table point must be a number."); + if (! y.numeric_type()) + y.throw_exception("Y value of a table point must be a number."); + table.table.push_back({ x.as_d(), x.it_range, y.as_d() }); + } } static void evaluate(const expr &expr_x, const InterpolateTableContext &table, expr &out) { + if (expr_x.type() == expr::TYPE_EMPTY) + return; + // Check whether the table X values are sorted. double x = expr_x.as_d(); bool evaluated = false; @@ -1698,6 +1840,52 @@ namespace client } }; + template + struct FactorActions { + static void set_start_pos(Iterator &start_pos, expr &out) + { out.it_range = boost::iterator_range(start_pos, start_pos); } + static void int_(const MyContext *ctx, int &value, Iterator &end_pos, expr &out) { + if (ctx->skipping()) { + out.reset(); + out.it_range.end() = end_pos; + } else + out = expr(value, out.it_range.begin(), end_pos); + } + static void double_(const MyContext *ctx, double &value, Iterator &end_pos, expr &out) { + if (ctx->skipping()) { + out.reset(); + out.it_range.end() = end_pos; + } else + out = expr(value, out.it_range.begin(), end_pos); + } + static void bool_(const MyContext *ctx, bool &value, Iterator &end_pos, expr &out) { + if (ctx->skipping()) { + out.reset(); + out.it_range.end() = end_pos; + } else + out = expr(value, out.it_range.begin(), end_pos); + } + static void string_(const MyContext *ctx, boost::iterator_range &it_range, expr &out) { + if (ctx->skipping()) { + out.reset(); + out.it_range = it_range; + } else + out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); + } + static void expr_(expr &value, Iterator &end_pos, expr &out) + { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } + static void minus_(expr &value, expr &out) + { out = value.unary_minus(out.it_range.begin()); } + static void not_(expr &value, expr &out) + { out = value.unary_not(out.it_range.begin()); } + static void to_int(expr &value, expr &out) + { out = value.unary_integer(out.it_range.begin()); } + static void round(expr &value, expr &out) + { out = value.round(out.it_range.begin()); } + // For indicating "no optional parameter". + static void noexpr(expr &out) { out.reset(); } + }; + /////////////////////////////////////////////////////////////////////////// // Our macro_processor grammar /////////////////////////////////////////////////////////////////////////// @@ -1771,19 +1959,19 @@ namespace client // | (kw["switch"] > switch_output(_r1) [_val = _1]) | (assignment_statement(_r1) [_val = _1]) | (new_variable_statement(_r1) [_val = _1]) - | (additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + | (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) ; macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). if_else_output = - eps[_b=true] > - bool_expr_eval(_r1)[_a=_1] > '}' > - text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)] > '{' > - *(kw["elsif"] > bool_expr_eval(_r1)[_a=_1] > '}' > - text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)] > '{') > - -(kw["else"] > lit('}') > - text_block(_r1)[px::bind(&expr::set_if, _b, _b, _1, _val)] > '{') > + eps[_a=true] > + (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > '}' > + text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{' > + *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > '}' > + text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{') > + -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > lit('}') > + text_block(_r1)[px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > '{') > kw["endif"]; if_else_output.name("if_else_output"); // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). @@ -1811,8 +1999,11 @@ namespace client identifier.name("identifier"); conditional_expression = - logical_or_expression(_r1) [_val = _1] - >> -('?' > conditional_expression(_r1) > ':' > conditional_expression(_r1)) [px::bind(&expr::ternary_op, _val, _1, _2)]; + logical_or_expression(_r1) [_val = _1] + >> -('?' > eps[px::bind(&expr::evaluate_boolean, _val, _a)] > + eps[px::bind(&MyContext::block_enter, _r1, _a)] > conditional_expression(_r1)[px::bind(&MyContext::block_exit_ternary>, _r1, _a, _1, _val)] + > ':' > + eps[px::bind(&MyContext::block_enter, _r1, ! _a)] > conditional_expression(_r1)[px::bind(&MyContext::block_exit_ternary>, _r1, ! _a, _1, _val)]); conditional_expression.name("conditional_expression"); logical_or_expression = @@ -1901,36 +2092,12 @@ namespace client ) ); - struct FactorActions { - static void set_start_pos(Iterator &start_pos, expr &out) - { out.it_range = boost::iterator_range(start_pos, start_pos); } - static void int_(int &value, Iterator &end_pos, expr &out) - { out = expr(value, out.it_range.begin(), end_pos); } - static void double_(double &value, Iterator &end_pos, expr &out) - { out = expr(value, out.it_range.begin(), end_pos); } - static void bool_(bool &value, Iterator &end_pos, expr &out) - { out = expr(value, out.it_range.begin(), end_pos); } - static void string_(boost::iterator_range &it_range, expr &out) - { out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); } - static void expr_(expr &value, Iterator &end_pos, expr &out) - { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } - static void minus_(expr &value, expr &out) - { out = value.unary_minus(out.it_range.begin()); } - static void not_(expr &value, expr &out) - { out = value.unary_not(out.it_range.begin()); } - static void to_int(expr &value, expr &out) - { out = value.unary_integer(out.it_range.begin()); } - static void round(expr &value, expr &out) - { out = value.round(out.it_range.begin()); } - // For indicating "no optional parameter". - static void noexpr(expr &out) { out.reset(); } - }; - unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( + unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( variable_reference(_r1) [px::bind(&MyContext::variable_value, _r1, _1, _val)] - | (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] - | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] - | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] - | ((kw["not"] | '!') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] + | (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] + | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | ((kw["not"] | '!') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] | (kw["min"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&expr::min, _val, _2) ] | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') @@ -1941,18 +2108,18 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["zdigits"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > optional_parameter(_r1)) [ px::bind(&expr::template digits, _val, _2, _3) ] - | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] - | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] + | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] + | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] | (kw["is_nil"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_nil_test, _r1, _1, _val)] | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] | (kw["empty"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_vector_empty, _r1, _1, _val)] | (kw["size"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::vector_size, _r1, _1, _val)] | (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ] - | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] - | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] - | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] + | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _r1, _1, _2, _val) ] + | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _r1, _1, _2, _val) ] + | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _r1, _1, _2, _val) ] | raw[lexeme['"' > *((utf8char - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] - [ px::bind(&FactorActions::string_, _1, _val) ] + [ px::bind(&FactorActions::string_, _r1, _1, _val) ] ); unary_expression.name("unary_expression"); @@ -1980,8 +2147,8 @@ namespace client [px::bind(&InterpolateTableContext::add_pair, _1, _2, _val)] >> -lit(',')) ); interpolate_table.name("interpolate_table_list"); - optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( - lit(')') [ px::bind(&FactorActions::noexpr, _val) ] + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( + lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] ); optional_parameter.name("optional_parameter"); @@ -2074,7 +2241,7 @@ namespace client // Parsed identifier name. qi::rule(), spirit_encoding::space_type> identifier; // Ternary operator (?:) over logical_or_expression. - RuleExpression conditional_expression; + qi::rule(const MyContext*), qi::locals, spirit_encoding::space_type> conditional_expression; // Logical or over logical_and_expressions. RuleExpression logical_or_expression; // Logical and over relational_expressions. @@ -2108,7 +2275,7 @@ namespace client qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> interpolate_table; qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> interpolate_table_list; - qi::rule, spirit_encoding::space_type> if_else_output; + qi::rule, spirit_encoding::space_type> if_else_output; qi::rule>, spirit_encoding::space_type> assignment_statement; // Allocating new local or global variables. qi::rule>, spirit_encoding::space_type> new_variable_statement; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index b29ca0f8e..0e3521a23 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -75,6 +75,11 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); } SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); } SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); } + SECTION("math: ternary1") { REQUIRE(parser.process("{12 == 12 ? 1 - 3 : 2 * 2 * unknown_symbol}") == "-2"); } + SECTION("math: ternary2") { REQUIRE(parser.process("{12 == 21/2 ? 1 - 1 - unknown_symbol : 2 * 2}") == "4"); } + SECTION("math: ternary3") { REQUIRE(parser.process("{12 == 13 ? 1 - 1 * unknown_symbol : 2 * 2}") == "4"); } + SECTION("math: ternary4") { REQUIRE(parser.process("{12 == 2 * 6 ? 1 - 1 : 2 * unknown_symbol}") == "0"); } + SECTION("math: ternary nested") { REQUIRE(parser.process("{12 == 2 * 6 ? 3 - 1 != 2 ? does_not_exist : 0 * 0 - 0 / 1 + 12345 : bull ? 3 - cokoo : 2 * unknown_symbol}") == "12345"); } SECTION("math: interpolate_table(13.84375892476, (0, 0), (20, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13.84375892476, (0, 0), (20, 20))}")) == Approx(13.84375892476)); } SECTION("math: interpolate_table(13, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13, (0, 0), (20, 20), (30, 20))}")) == Approx(13.)); } SECTION("math: interpolate_table(25, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(25, (0, 0), (20, 20), (30, 20))}")) == Approx(20.)); } @@ -106,10 +111,10 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("boolean expression parser: (12 == 12) or (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) or (13 == 14)")); } SECTION("boolean expression parser: (12 == 12) || (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) || (13 == 14)")); } SECTION("boolean expression parser: (12 == 12) and not (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) and not (13 == 14)")); } - SECTION("boolean expression parser: ternary true") { REQUIRE(boolean_expression("(12 == 12) ? (1 - 1 == 0) : (2 * 2 == 3)")); } - SECTION("boolean expression parser: ternary false") { REQUIRE(! boolean_expression("(12 == 21/2) ? (1 - 1 == 0) : (2 * 2 == 3)")); } - SECTION("boolean expression parser: ternary false 2") { REQUIRE(boolean_expression("(12 == 13) ? (1 - 1 == 3) : (2 * 2 == 4)")); } - SECTION("boolean expression parser: ternary true 2") { REQUIRE(! boolean_expression("(12 == 2 * 6) ? (1 - 1 == 3) : (2 * 2 == 4)")); } + SECTION("boolean expression parser: ternary true") { REQUIRE(boolean_expression("(12 == 12) ? (1 - 1 == 0) : (2 * 2 == 3 * unknown_symbol)")); } + SECTION("boolean expression parser: ternary false") { REQUIRE(! boolean_expression("(12 == 21/2) ? (1 - 1 == 0 - unknown_symbol) : (2 * 2 == 3)")); } + SECTION("boolean expression parser: ternary false 2") { REQUIRE(boolean_expression("(12 == 13) ? (1 - 1 == 3 * unknown_symbol) : (2 * 2 == 4)")); } + SECTION("boolean expression parser: ternary true 2") { REQUIRE(! boolean_expression("(12 == 2 * 6) ? (1 - 1 == 3) : (2 * 2 == 4 * unknown_symbol)")); } SECTION("boolean expression parser: lower than - false") { REQUIRE(! boolean_expression("12 < 3")); } SECTION("boolean expression parser: lower than - true") { REQUIRE(boolean_expression("12 < 22")); } SECTION("boolean expression parser: greater than - true") { REQUIRE(boolean_expression("12 > 3")); } @@ -227,4 +232,23 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { SECTION("size() of a an empty vector returns the right size") { REQUIRE(parser.process("{local myint = (0);myint=();size(myint)}", 0, nullptr, nullptr, nullptr) == "0"); } SECTION("empty() of a non-empty vector returns false") { REQUIRE(parser.process("{local myint = (0, 1, 2, 3)}{empty(myint)}", 0, nullptr, nullptr, nullptr) == "false"); } SECTION("empty() of a an empty vector returns true") { REQUIRE(parser.process("{local myint = (0);myint=();empty(myint)}", 0, nullptr, nullptr, nullptr) == "true"); } + + SECTION("nested if with new variables") { + std::string script = + "{if 1 == 1}{local myints = (5, 4, 3, 2, 1)}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}" + "{myints[1]},{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "4,5"); + } + SECTION("nested if with new variables 2") { + std::string script = + "{if 1 == 0}{local myints = (5, 4, 3, 2, 1)}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}" + "{size(myfloats)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); + } + SECTION("nested if with new variables, two level") { + std::string script = + "{if 1 == 1}{if 2 == 3}{nejaka / haluz}{else}{local myints = (6, 5, 4, 3, 2, 1)}{endif}{else}{if zase * haluz}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}{endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } } From 59d3683a799f342c3ed9217ee3d1d4ceabdcdc17 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 24 Mar 2023 14:26:25 +0100 Subject: [PATCH 164/201] PlaceholderParser: Refactored, removed all the unncesseary template parameters. --- src/libslic3r/PlaceholderParser.cpp | 566 ++++++++++++---------------- 1 file changed, 237 insertions(+), 329 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 06a4d3960..fb9fdbea0 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -167,28 +167,28 @@ namespace px = boost::phoenix; namespace client { - template + using Iterator = std::string::const_iterator; + using IteratorRange = boost::iterator_range; + struct OptWithPos { OptWithPos() {} - OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range, bool writable = false) : opt(opt), it_range(it_range), writable(writable) {} + OptWithPos(ConfigOptionConstPtr opt, IteratorRange it_range, bool writable = false) : opt(opt), it_range(it_range), writable(writable) {} ConfigOptionConstPtr opt { nullptr }; bool writable { false }; // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. int index { -1 }; - boost::iterator_range it_range; + IteratorRange it_range; bool empty() const { return opt == nullptr; } bool has_index() const { return index != -1; } }; - template - std::ostream& operator<<(std::ostream& os, OptWithPos const& opt) + std::ostream& operator<<(std::ostream& os, OptWithPos const& opt) { os << std::string(opt.it_range.begin(), opt.it_range.end()); return os; } - template struct expr { expr() {} @@ -320,7 +320,7 @@ namespace client // Range of input iterators covering this expression. // Used for throwing parse exceptions. - boost::iterator_range it_range; + IteratorRange it_range; expr unary_minus(const Iterator start_pos) const { @@ -329,9 +329,9 @@ namespace client // Inside an if / else block to be skipped. return expr(); case TYPE_INT : - return expr(- this->i(), start_pos, this->it_range.end()); + return expr(- this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: - return expr(- this->d(), start_pos, this->it_range.end()); + return expr(- this->d(), start_pos, this->it_range.end()); default: this->throw_exception("Cannot apply unary minus operator."); } @@ -347,9 +347,9 @@ namespace client // Inside an if / else block to be skipped. return expr(); case TYPE_INT: - return expr(this->i(), start_pos, this->it_range.end()); + return expr(this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: - return expr(static_cast(this->d()), start_pos, this->it_range.end()); + return expr(static_cast(this->d()), start_pos, this->it_range.end()); default: this->throw_exception("Cannot convert to integer."); } @@ -365,9 +365,9 @@ namespace client // Inside an if / else block to be skipped. return expr(); case TYPE_INT: - return expr(this->i(), start_pos, this->it_range.end()); + return expr(this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: - return expr(static_cast(std::round(this->d())), start_pos, this->it_range.end()); + return expr(static_cast(std::round(this->d())), start_pos, this->it_range.end()); default: this->throw_exception("Cannot round a non-numeric value."); } @@ -383,7 +383,7 @@ namespace client // Inside an if / else block to be skipped. return expr(); case TYPE_BOOL: - return expr(! this->b(), start_pos, this->it_range.end()); + return expr(! this->b(), start_pos, this->it_range.end()); default: this->throw_exception("Cannot apply a not operator."); } @@ -411,7 +411,7 @@ namespace client else m_data.i += rhs.i(); } - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + this->it_range = IteratorRange(this->it_range.begin(), rhs.it_range.end()); return *this; } @@ -428,7 +428,7 @@ namespace client this->set_d_lite(this->as_d() - rhs.as_d()); else m_data.i -= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + this->it_range = IteratorRange(this->it_range.begin(), rhs.it_range.end()); } return *this; } @@ -446,7 +446,7 @@ namespace client this->set_d_lite(this->as_d() * rhs.as_d()); else m_data.i *= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + this->it_range = IteratorRange(this->it_range.begin(), rhs.it_range.end()); } return *this; } @@ -465,7 +465,7 @@ namespace client this->set_d_lite(this->as_d() / rhs.as_d()); else m_data.i /= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + this->it_range = IteratorRange(this->it_range.begin(), rhs.it_range.end()); } return *this; } @@ -484,7 +484,7 @@ namespace client this->set_d_lite(std::fmod(this->as_d(), rhs.as_d())); else m_data.i %= rhs.i(); - this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + this->it_range = IteratorRange(this->it_range.begin(), rhs.it_range.end()); } return *this; } @@ -644,7 +644,7 @@ namespace client param1.set_s(buf); } - static void regex_op(const expr &lhs, boost::iterator_range &rhs, char op, expr &out) + static void regex_op(const expr &lhs, IteratorRange &rhs, char op, expr &out) { if (lhs.type() == TYPE_EMPTY) // Inside an if / else block to be skipped @@ -669,8 +669,8 @@ namespace client } } - static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '=', lhs); } - static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!', lhs); } + static void regex_matches (expr &lhs, IteratorRange &rhs) { return regex_op(lhs, rhs, '=', lhs); } + static void regex_doesnt_match(expr &lhs, IteratorRange &rhs) { return regex_op(lhs, rhs, '!', lhs); } static void one_of_test_init(expr &out) { out.set_b(false); @@ -698,7 +698,7 @@ namespace client out.set_b(match.s() == pattern.s()); } } - static void one_of_test_regex(const expr &match, boost::iterator_range &pattern, expr &out) { + static void one_of_test_regex(const expr &match, IteratorRange &pattern, expr &out) { if (match.type() == TYPE_EMPTY) { // Inside an if / else block to be skipped out.reset(); @@ -757,10 +757,9 @@ namespace client } m_data; }; - template - std::ostream& operator<<(std::ostream &os, const expr &expression) + std::ostream& operator<<(std::ostream &os, const expr &expression) { - typedef expr Expr; + typedef expr Expr; os << std::string(expression.it_range.begin(), expression.it_range.end()) << " - "; switch (expression.type()) { case Expr::TYPE_EMPTY: os << "empty"; break; @@ -815,8 +814,7 @@ namespace client not_yet_consumed = false; } } - template - static void block_exit_ternary(const MyContext* ctx, const bool condition, DataType &data_in, DataType &data_out) + static void block_exit_ternary(const MyContext* ctx, const bool condition, expr &data_in, expr &data_out) { if (ctx->skipping()) -- ctx->m_depth_suppressed; @@ -858,11 +856,7 @@ namespace client this->config_local.set_key_value(opt_key, opt.release()); } - template - static void legacy_variable_expansion( - const MyContext *ctx, - boost::iterator_range &opt_key, - std::string &output) + static void legacy_variable_expansion(const MyContext *ctx, IteratorRange &opt_key, std::string &output) { if (ctx->skipping()) return; @@ -881,12 +875,12 @@ namespace client char *endptr = nullptr; idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10); if (endptr == nullptr || *endptr != 0) - ctx->throw_exception("Invalid vector index", boost::iterator_range(opt_key.begin() + idx + 1, opt_key.end())); + ctx->throw_exception("Invalid vector index", IteratorRange(opt_key.begin() + idx + 1, opt_key.end())); } } } if (opt == nullptr) - ctx->throw_exception("Variable does not exist", boost::iterator_range(opt_key.begin(), opt_key.end())); + ctx->throw_exception("Variable does not exist", IteratorRange(opt_key.begin(), opt_key.end())); if (opt->is_scalar()) output = opt->serialize(); else { @@ -897,12 +891,11 @@ namespace client } } - template static void legacy_variable_expansion2( - const MyContext *ctx, - boost::iterator_range &opt_key, - boost::iterator_range &opt_vector_index, - std::string &output) + const MyContext *ctx, + IteratorRange &opt_key, + IteratorRange &opt_vector_index, + std::string &output) { if (ctx->skipping()) return; @@ -919,7 +912,7 @@ namespace client ctx->throw_exception("Trying to index a scalar variable", opt_key); const ConfigOptionVectorBase *vec = static_cast(opt); if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", boost::iterator_range(opt_key.begin(), opt_key.end())); + ctx->throw_exception("Indexing an empty vector variable", IteratorRange(opt_key.begin(), opt_key.end())); const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end())); if (opt_index == nullptr) ctx->throw_exception("Variable does not exist", opt_key); @@ -931,11 +924,10 @@ namespace client output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx]; } - template static void resolve_variable( - const MyContext *ctx, - boost::iterator_range &opt_key, - OptWithPos &output) + const MyContext *ctx, + IteratorRange &opt_key, + OptWithPos &output) { if (! ctx->skipping()) { const std::string key{ opt_key.begin(), opt_key.end() }; @@ -951,13 +943,12 @@ namespace client output.it_range = opt_key; } - template static void store_variable_index( - const MyContext *ctx, - OptWithPos &opt, - int index, - Iterator it_end, - OptWithPos &output) + const MyContext *ctx, + OptWithPos &opt, + int index, + Iterator it_end, + OptWithPos &output) { if (! ctx->skipping()) { if (! opt.opt->is_vector()) @@ -973,11 +964,7 @@ namespace client // Evaluating a scalar variable into expr, // all possible ConfigOption types are supported. - template - static void scalar_variable_to_expr( - const MyContext *ctx, - OptWithPos &opt, - expr &output) + static void scalar_variable_to_expr(const MyContext *ctx, OptWithPos &opt, expr &output) { if (ctx->skipping()) return; @@ -1037,11 +1024,7 @@ namespace client // Evaluating one element of a vector variable. // all possible ConfigOption types are supported. - template - static void vector_element_to_expr( - const MyContext *ctx, - OptWithPos &opt, - expr &output) + static void vector_element_to_expr(const MyContext *ctx, OptWithPos &opt, expr &output) { if (ctx->skipping()) return; @@ -1066,21 +1049,18 @@ namespace client } } - template - static void check_writable(const MyContext *ctx, OptWithPos &opt) { + static void check_writable(const MyContext *ctx, OptWithPos &opt) { if (! opt.writable) ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); } - template - static void check_numeric(const expr ¶m) { + static void check_numeric(const expr ¶m) { if (! param.numeric_type()) param.throw_exception("Right side is not a numeric expression"); }; - template - static size_t evaluate_count(const expr &expr_count) { - if (expr_count.type() != expr::TYPE_INT) + static size_t evaluate_count(const expr &expr_count) { + if (expr_count.type() != expr::TYPE_INT) expr_count.throw_exception("Expected number of elements to fill a vector with."); int count = expr_count.i(); if (count < 0) @@ -1088,11 +1068,7 @@ namespace client return size_t(count); }; - template - static void scalar_variable_assign_scalar( - const MyContext *ctx, - OptWithPos &lhs, - const expr &rhs) + static void scalar_variable_assign_scalar(const MyContext *ctx, OptWithPos &lhs, const expr &rhs) { assert(lhs.opt->is_scalar()); check_writable(ctx, lhs); @@ -1114,7 +1090,7 @@ namespace client static_cast(wropt)->value = rhs.as_d(); break; case coBool: - if (rhs.type() != expr::TYPE_BOOL) + if (rhs.type() != expr::TYPE_BOOL) ctx->throw_exception("Right side is not a boolean expression", rhs.it_range); static_cast(wropt)->value = rhs.b(); break; @@ -1123,11 +1099,7 @@ namespace client } } - template - static void vector_variable_element_assign_scalar( - const MyContext *ctx, - OptWithPos &lhs, - const expr &rhs) + static void vector_variable_element_assign_scalar(const MyContext *ctx, OptWithPos &lhs, const expr &rhs) { assert(lhs.opt->is_vector()); check_writable(ctx, lhs); @@ -1155,7 +1127,7 @@ namespace client static_cast(vec)->values[lhs.index] = rhs.as_d(); break; case coBools: - if (rhs.type() != expr::TYPE_BOOL) + if (rhs.type() != expr::TYPE_BOOL) ctx->throw_exception("Right side is not a boolean expression", rhs.it_range); static_cast(vec)->values[lhs.index] = rhs.b(); break; @@ -1164,12 +1136,7 @@ namespace client } } - template - static void vector_variable_assign_expr_with_count( - const MyContext *ctx, - OptWithPos &lhs, - const expr &rhs_count, - const expr &rhs_value) + static void vector_variable_assign_expr_with_count(const MyContext *ctx, OptWithPos &lhs, const expr &rhs_count, const expr &rhs_value) { size_t count = evaluate_count(rhs_count); auto *opt = const_cast(lhs.opt); @@ -1186,7 +1153,7 @@ namespace client static_cast(opt)->values.assign(count, rhs_value.to_string()); break; case coBools: - if (rhs_value.type() != expr::TYPE_BOOL) + if (rhs_value.type() != expr::TYPE_BOOL) rhs_value.throw_exception("Right side is not a boolean expression"); static_cast(opt)->values.assign(count, rhs_value.b()); break; @@ -1194,11 +1161,7 @@ namespace client } } - template - static void variable_value( - const MyContext *ctx, - OptWithPos &opt, - expr &output) + static void variable_value(const MyContext *ctx, OptWithPos &opt, expr &output) { if (! ctx->skipping()) { if (opt.opt->is_vector()) @@ -1211,11 +1174,7 @@ namespace client // Return a boolean value, true if the scalar variable referenced by "opt" is nullable and it has a nil value. // Return a boolean value, true if an element of a vector variable referenced by "opt[index]" is nullable and it has a nil value. - template - static void is_nil_test( - const MyContext *ctx, - OptWithPos &opt, - expr &output) + static void is_nil_test(const MyContext *ctx, OptWithPos &opt, expr &output) { if (ctx->skipping()) { } else if (opt.opt->is_vector()) { @@ -1233,18 +1192,16 @@ namespace client } // Reference to an existing symbol, or a name of a new symbol. - template struct NewOldVariable { - std::string name; - boost::iterator_range it_range; - ConfigOption *opt{ nullptr }; + std::string name; + IteratorRange it_range; + ConfigOption *opt{ nullptr }; }; - template static void new_old_variable( - const MyContext *ctx, - bool global_variable, - const boost::iterator_range &it_range, - NewOldVariable &out) + const MyContext *ctx, + bool global_variable, + const IteratorRange &it_range, + NewOldVariable &out) { if (! ctx->skipping()) { t_config_option_key key(std::string(it_range.begin(), it_range.end())); @@ -1270,11 +1227,7 @@ namespace client } // Decoding a scalar variable symbol "opt", assigning it a value of "param". - template - static void scalar_variable_assign_scalar_expression( - const MyContext *ctx, - OptWithPos &opt, - const expr ¶m) + static void scalar_variable_assign_scalar_expression(const MyContext *ctx, OptWithPos &opt, const expr ¶m) { if (! ctx->skipping()) { check_writable(ctx, opt); @@ -1285,12 +1238,11 @@ namespace client } } - template static void scalar_variable_new_from_scalar_expression( - const MyContext *ctx, - bool global_variable, - NewOldVariable &lhs, - const expr &rhs) + const MyContext *ctx, + bool global_variable, + NewOldVariable &lhs, + const expr &rhs) { if (ctx->skipping()) { } else if (lhs.opt) { @@ -1301,23 +1253,22 @@ namespace client } else { std::unique_ptr opt_new; switch (rhs.type()) { - case expr::TYPE_BOOL: opt_new = std::make_unique(rhs.b()); break; - case expr::TYPE_INT: opt_new = std::make_unique(rhs.i()); break; - case expr::TYPE_DOUBLE: opt_new = std::make_unique(rhs.d()); break; - case expr::TYPE_STRING: opt_new = std::make_unique(rhs.s()); break; + case expr::TYPE_BOOL: opt_new = std::make_unique(rhs.b()); break; + case expr::TYPE_INT: opt_new = std::make_unique(rhs.i()); break; + case expr::TYPE_DOUBLE: opt_new = std::make_unique(rhs.d()); break; + case expr::TYPE_STRING: opt_new = std::make_unique(rhs.s()); break; default: assert(false); } const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); } } - template static void vector_variable_new_from_array( - const MyContext *ctx, - bool global_variable, - NewOldVariable &lhs, - const expr &rhs_count, - const expr &rhs_value) + const MyContext *ctx, + bool global_variable, + NewOldVariable &lhs, + const expr &rhs_count, + const expr &rhs_value) { if (ctx->skipping()) { } else if (lhs.opt) { @@ -1329,22 +1280,21 @@ namespace client size_t count = evaluate_count(rhs_count); std::unique_ptr opt_new; switch (rhs_value.type()) { - case expr::TYPE_BOOL: opt_new = std::make_unique(count, rhs_value.b()); break; - case expr::TYPE_INT: opt_new = std::make_unique(count, rhs_value.i()); break; - case expr::TYPE_DOUBLE: opt_new = std::make_unique(count, rhs_value.d()); break; - case expr::TYPE_STRING: opt_new = std::make_unique(count, rhs_value.s()); break; + case expr::TYPE_BOOL: opt_new = std::make_unique(count, rhs_value.b()); break; + case expr::TYPE_INT: opt_new = std::make_unique(count, rhs_value.i()); break; + case expr::TYPE_DOUBLE: opt_new = std::make_unique(count, rhs_value.d()); break; + case expr::TYPE_STRING: opt_new = std::make_unique(count, rhs_value.s()); break; default: assert(false); } const_cast(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable); } } - template static void vector_variable_assign_array( - const MyContext *ctx, - OptWithPos &lhs, - const expr &rhs_count, - const expr &rhs_value) + const MyContext *ctx, + OptWithPos &lhs, + const expr &rhs_count, + const expr &rhs_value) { if (! ctx->skipping()) { check_writable(ctx, lhs); @@ -1354,20 +1304,16 @@ namespace client } } - template - static void fill_vector_from_initializer_list(ConfigOption *opt, const std::vector> &il, RightValueEvaluate rv_eval) { + template + static void fill_vector_from_initializer_list(ConfigOption *opt, const std::vector &il, RightValueEvaluate rv_eval) { auto& out = static_cast(opt)->values; out.clear(); out.reserve(il.size()); - for (const expr& i : il) + for (const expr& i : il) out.emplace_back(rv_eval(i)); } - template - static void vector_variable_assign_initializer_list( - const MyContext *ctx, - OptWithPos &lhs, - const std::vector> &il) + static void vector_variable_assign_initializer_list(const MyContext *ctx, OptWithPos &lhs, const std::vector &il) { if (ctx->skipping()) return; @@ -1385,7 +1331,7 @@ namespace client ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range); } - auto check_numeric_vector = [](const std::vector> &il) { + auto check_numeric_vector = [](const std::vector &il) { for (auto &i : il) if (! i.numeric_type()) i.throw_exception("Right side is not a numeric expression"); @@ -1406,7 +1352,7 @@ namespace client break; case coBools: for (auto &i : il) - if (i.type() != expr::TYPE_BOOL) + if (i.type() != expr::TYPE_BOOL) i.throw_exception("Right side is not a boolean expression"); fill_vector_from_initializer_list(opt, il, [](auto &v){ return v.b(); }); break; @@ -1414,12 +1360,11 @@ namespace client } } - template static void vector_variable_new_from_initializer_list( - const MyContext *ctx, - bool global_variable, - NewOldVariable &lhs, - const std::vector> &il) + const MyContext *ctx, + bool global_variable, + NewOldVariable &lhs, + const std::vector &il) { if (ctx->skipping()) return; @@ -1439,10 +1384,10 @@ namespace client size_t num_string = 0; for (auto &i : il) switch (i.type()) { - case expr::TYPE_BOOL: ++ num_bool; break; - case expr::TYPE_INT: ++ num_int; break; - case expr::TYPE_DOUBLE: ++ num_double; break; - case expr::TYPE_STRING: ++ num_string; break; + case expr::TYPE_BOOL: ++ num_bool; break; + case expr::TYPE_INT: ++ num_int; break; + case expr::TYPE_DOUBLE: ++ num_double; break; + case expr::TYPE_STRING: ++ num_string; break; default: assert(false); } std::unique_ptr opt_new; @@ -1451,7 +1396,7 @@ namespace client opt_new = std::make_unique(); else if (num_bool > 0) { if (num_double + num_int > 0) - ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range{ il.front().it_range.begin(), il.back().it_range.end() }); + ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", IteratorRange{ il.front().it_range.begin(), il.back().it_range.end() }); opt_new = std::make_unique(); } else { // Output is numeric. @@ -1466,22 +1411,16 @@ namespace client } } - template - static bool is_vector_variable_reference(const OptWithPos &var) { + static bool is_vector_variable_reference(const OptWithPos &var) { return ! var.empty() && ! var.has_index() && var.opt->is_vector(); } // Called when checking whether the NewOldVariable could be assigned a vectir right hand side. - template - static bool could_be_vector_variable_reference(const NewOldVariable &var) { + static bool could_be_vector_variable_reference(const NewOldVariable &var) { return var.opt == nullptr || var.opt->is_vector(); } - template - static void copy_vector_variable_to_vector_variable( - const MyContext *ctx, - OptWithPos &lhs, - const OptWithPos &rhs) + static void copy_vector_variable_to_vector_variable(const MyContext *ctx, OptWithPos &lhs, const OptWithPos &rhs) { if (ctx->skipping()) return; @@ -1508,12 +1447,11 @@ namespace client const_cast(lhs.opt)->set(rhs.opt); } - template static bool vector_variable_new_from_copy( - const MyContext *ctx, - bool global_variable, - NewOldVariable &lhs, - const OptWithPos &rhs) + const MyContext *ctx, + bool global_variable, + NewOldVariable &lhs, + const OptWithPos &rhs) { if (ctx->skipping()) // Skipping, continue parsing. @@ -1541,19 +1479,14 @@ namespace client return true; } - template - static void initializer_list_append(std::vector> &list, expr ¶m) + static void initializer_list_append(std::vector &list, expr ¶m) { - if (param.type() != expr::TYPE_EMPTY) + if (param.type() != expr::TYPE_EMPTY) // not skipping list.emplace_back(std::move(param)); } - template - static void is_vector_empty( - const MyContext *ctx, - OptWithPos &opt, - expr &out) + static void is_vector_empty(const MyContext *ctx, OptWithPos &opt, expr &out) { if (! ctx->skipping()) { if (opt.has_index() || ! opt.opt->is_vector()) @@ -1563,11 +1496,7 @@ namespace client out.it_range = opt.it_range; } - template - static void vector_size( - const MyContext *ctx, - OptWithPos &opt, - expr &out) + static void vector_size(const MyContext *ctx, OptWithPos &opt, expr &out) { if (! ctx->skipping()) { if (opt.has_index() || ! opt.opt->is_vector()) @@ -1579,37 +1508,33 @@ namespace client // Verify that the expression returns an integer, which may be used // to address a vector. - template - static void evaluate_index(expr &expr_index, int &output) + static void evaluate_index(expr &expr_index, int &output) { - if (expr_index.type() != expr::TYPE_EMPTY) { - if (expr_index.type() != expr::TYPE_INT) + if (expr_index.type() != expr::TYPE_EMPTY) { + if (expr_index.type() != expr::TYPE_INT) expr_index.throw_exception("Non-integer index is not allowed to address a vector variable."); output = expr_index.i(); } } - template - static void random(const MyContext *ctx, expr ¶m1, expr ¶m2) + static void random(const MyContext *ctx, expr ¶m1, expr ¶m2) { if (ctx->skipping()) return; if (ctx->context_data == nullptr) ctx->throw_exception("Random number generator not available in this context.", - boost::iterator_range(param1.it_range.begin(), param2.it_range.end())); - expr::random(param1, param2, ctx->context_data->rng); + IteratorRange(param1.it_range.begin(), param2.it_range.end())); + expr::random(param1, param2, ctx->context_data->rng); } - template - static void throw_exception(const std::string &msg, const boost::iterator_range &it_range) + static void throw_exception(const std::string &msg, const IteratorRange &it_range) { // An asterix is added to the start of the string to differentiate the boost::spirit::info::tag content // between the grammer terminal / non-terminal symbol name and a free-form error message. - boost::throw_exception(qi::expectation_failure(it_range.begin(), it_range.end(), spirit::info(std::string("*") + msg))); + boost::throw_exception(qi::expectation_failure(it_range.begin(), it_range.end(), spirit::info(std::string("*") + msg))); } - template static void process_error_message(const MyContext *context, const boost::spirit::info &info, const Iterator &it_begin, const Iterator &it_end, const Iterator &it_error) { std::string &msg = const_cast(context)->error_message; @@ -1662,23 +1587,22 @@ namespace client mutable int m_depth_suppressed{ 0 }; }; - template struct InterpolateTableContext { struct Item { - double x; - boost::iterator_range it_range_x; - double y; + double x; + IteratorRange it_range_x; + double y; }; std::vector table; - static void init(const expr &x) { - if (x.type() != expr::TYPE_EMPTY) { + static void init(const expr &x) { + if (x.type() != expr::TYPE_EMPTY) { if (!x.numeric_type()) x.throw_exception("Interpolation value must be a number."); } } - static void add_pair(const expr &x, const expr &y, InterpolateTableContext &table) { - if (x.type() != expr::TYPE_EMPTY) { + static void add_pair(const expr &x, const expr &y, InterpolateTableContext &table) { + if (x.type() != expr::TYPE_EMPTY) { if (! x.numeric_type()) x.throw_exception("X value of a table point must be a number."); if (! y.numeric_type()) @@ -1686,8 +1610,8 @@ namespace client table.table.push_back({ x.as_d(), x.it_range, y.as_d() }); } } - static void evaluate(const expr &expr_x, const InterpolateTableContext &table, expr &out) { - if (expr_x.type() == expr::TYPE_EMPTY) + static void evaluate(const expr &expr_x, const InterpolateTableContext &table, expr &out) { + if (expr_x.type() == expr::TYPE_EMPTY) return; // Check whether the table X values are sorted. @@ -1697,7 +1621,7 @@ namespace client double x0 = table.table[i - 1].x; double x1 = table.table[i].x; if (x0 > x1) - boost::throw_exception(qi::expectation_failure( + boost::throw_exception(qi::expectation_failure( table.table[i - 1].it_range_x.begin(), table.table[i].it_range_x.end(), spirit::info("X coordinates of the table must be increasing"))); if (! evaluated && x >= x0 && x <= x1) { double y0 = table.table[i - 1].y; @@ -1726,8 +1650,7 @@ namespace client } }; - template - std::ostream& operator<<(std::ostream &os, const InterpolateTableContext &table_context) + std::ostream& operator<<(std::ostream &os, const InterpolateTableContext &table_context) { for (const auto &item : table_context.table) os << "(" << item.x << "," << item.y << ")"; @@ -1763,8 +1686,7 @@ namespace client }; // For debugging the boost::spirit parsers. Print out the string enclosed in it_range. - template - std::ostream& operator<<(std::ostream& os, const boost::iterator_range &it_range) + std::ostream& operator<<(std::ostream& os, const IteratorRange &it_range) { os << std::string(it_range.begin(), it_range.end()); return os; @@ -1828,7 +1750,7 @@ namespace client first = it; return true; err: - MyContext::throw_exception("Invalid utf8 sequence", boost::iterator_range(first, last)); + MyContext::throw_exception("Invalid utf8 sequence", IteratorRange(first, last)); return false; } @@ -1840,57 +1762,55 @@ namespace client } }; - template struct FactorActions { - static void set_start_pos(Iterator &start_pos, expr &out) - { out.it_range = boost::iterator_range(start_pos, start_pos); } - static void int_(const MyContext *ctx, int &value, Iterator &end_pos, expr &out) { + static void set_start_pos(Iterator &start_pos, expr &out) + { out.it_range = IteratorRange(start_pos, start_pos); } + static void int_(const MyContext *ctx, int &value, Iterator &end_pos, expr &out) { if (ctx->skipping()) { out.reset(); out.it_range.end() = end_pos; } else - out = expr(value, out.it_range.begin(), end_pos); + out = expr(value, out.it_range.begin(), end_pos); } - static void double_(const MyContext *ctx, double &value, Iterator &end_pos, expr &out) { + static void double_(const MyContext *ctx, double &value, Iterator &end_pos, expr &out) { if (ctx->skipping()) { out.reset(); out.it_range.end() = end_pos; } else - out = expr(value, out.it_range.begin(), end_pos); + out = expr(value, out.it_range.begin(), end_pos); } - static void bool_(const MyContext *ctx, bool &value, Iterator &end_pos, expr &out) { + static void bool_(const MyContext *ctx, bool &value, Iterator &end_pos, expr &out) { if (ctx->skipping()) { out.reset(); out.it_range.end() = end_pos; } else - out = expr(value, out.it_range.begin(), end_pos); + out = expr(value, out.it_range.begin(), end_pos); } - static void string_(const MyContext *ctx, boost::iterator_range &it_range, expr &out) { + static void string_(const MyContext *ctx, IteratorRange &it_range, expr &out) { if (ctx->skipping()) { out.reset(); out.it_range = it_range; } else - out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); + out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); } - static void expr_(expr &value, Iterator &end_pos, expr &out) - { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } - static void minus_(expr &value, expr &out) + static void expr_(expr &value, Iterator &end_pos, expr &out) + { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } + static void minus_(expr &value, expr &out) { out = value.unary_minus(out.it_range.begin()); } - static void not_(expr &value, expr &out) + static void not_(expr &value, expr &out) { out = value.unary_not(out.it_range.begin()); } - static void to_int(expr &value, expr &out) + static void to_int(expr &value, expr &out) { out = value.unary_integer(out.it_range.begin()); } - static void round(expr &value, expr &out) + static void round(expr &value, expr &out) { out = value.round(out.it_range.begin()); } // For indicating "no optional parameter". - static void noexpr(expr &out) { out.reset(); } + static void noexpr(expr &out) { out.reset(); } }; /////////////////////////////////////////////////////////////////////////// // Our macro_processor grammar /////////////////////////////////////////////////////////////////////////// // Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html - template struct macro_processor : qi::grammar, spirit_encoding::space_type> { macro_processor() : macro_processor::base_type(start) @@ -1932,10 +1852,10 @@ namespace client // could serve both purposes. start = ( (eps(px::bind(&MyContext::evaluate_full_macro, _r1)) > text_block(_r1) [_val=_1]) - | conditional_expression(_r1) [ px::bind(&expr::evaluate_boolean_to_string, _1, _val) ] + | conditional_expression(_r1) [ px::bind(&expr::evaluate_boolean_to_string, _1, _val) ] ) > eoi; start.name("start"); - qi::on_error(start, px::bind(&MyContext::process_error_message, _r1, _4, _1, _2, _3)); + qi::on_error(start, px::bind(&MyContext::process_error_message, _r1, _4, _1, _2, _3)); text_block = *( text [_val+=_1] @@ -1959,7 +1879,7 @@ namespace client // | (kw["switch"] > switch_output(_r1) [_val = _1]) | (assignment_statement(_r1) [_val = _1]) | (new_variable_statement(_r1) [_val = _1]) - | (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + | (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) ; macro.name("macro"); @@ -1978,18 +1898,18 @@ namespace client /* switch_output = eps[_b=true] > - omit[expr(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr::set_if_equal, _a, _b, _1, _val)] > '{' > - *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)]) >> - -("else" > '}' >> text_block(_r1)[px::bind(&expr::set_if, _b, _b, _1, _val)]) > + omit[expr(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr::set_if_equal, _a, _b, _1, _val)] > '{' > + *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)]) >> + -("else" > '}' >> text_block(_r1)[px::bind(&expr::set_if, _b, _b, _1, _val)]) > "endif"; */ // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. legacy_variable_expansion = (identifier >> &lit(']')) - [ px::bind(&MyContext::legacy_variable_expansion, _r1, _1, _val) ] + [ px::bind(&MyContext::legacy_variable_expansion, _r1, _1, _val) ] | (identifier > lit('[') > identifier > ']') - [ px::bind(&MyContext::legacy_variable_expansion2, _r1, _1, _2, _val) ] + [ px::bind(&MyContext::legacy_variable_expansion2, _r1, _1, _2, _val) ] ; legacy_variable_expansion.name("legacy_variable_expansion"); @@ -2000,43 +1920,43 @@ namespace client conditional_expression = logical_or_expression(_r1) [_val = _1] - >> -('?' > eps[px::bind(&expr::evaluate_boolean, _val, _a)] > - eps[px::bind(&MyContext::block_enter, _r1, _a)] > conditional_expression(_r1)[px::bind(&MyContext::block_exit_ternary>, _r1, _a, _1, _val)] + >> -('?' > eps[px::bind(&expr::evaluate_boolean, _val, _a)] > + eps[px::bind(&MyContext::block_enter, _r1, _a)] > conditional_expression(_r1)[px::bind(&MyContext::block_exit_ternary, _r1, _a, _1, _val)] > ':' > - eps[px::bind(&MyContext::block_enter, _r1, ! _a)] > conditional_expression(_r1)[px::bind(&MyContext::block_exit_ternary>, _r1, ! _a, _1, _val)]); + eps[px::bind(&MyContext::block_enter, _r1, ! _a)] > conditional_expression(_r1)[px::bind(&MyContext::block_exit_ternary, _r1, ! _a, _1, _val)]); conditional_expression.name("conditional_expression"); logical_or_expression = logical_and_expression(_r1) [_val = _1] - >> *( ((kw["or"] | "||") > logical_and_expression(_r1) ) [px::bind(&expr::logical_or, _val, _1)] ); + >> *( ((kw["or"] | "||") > logical_and_expression(_r1) ) [px::bind(&expr::logical_or, _val, _1)] ); logical_or_expression.name("logical_or_expression"); logical_and_expression = equality_expression(_r1) [_val = _1] - >> *( ((kw["and"] | "&&") > equality_expression(_r1) ) [px::bind(&expr::logical_and, _val, _1)] ); + >> *( ((kw["and"] | "&&") > equality_expression(_r1) ) [px::bind(&expr::logical_and, _val, _1)] ); logical_and_expression.name("logical_and_expression"); equality_expression = relational_expression(_r1) [_val = _1] - >> *( ("==" > relational_expression(_r1) ) [px::bind(&expr::equal, _val, _1)] - | ("!=" > relational_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] - | ("<>" > relational_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] - | ("=~" > regular_expression ) [px::bind(&expr::regex_matches, _val, _1)] - | ("!~" > regular_expression ) [px::bind(&expr::regex_doesnt_match, _val, _1)] + >> *( ("==" > relational_expression(_r1) ) [px::bind(&expr::equal, _val, _1)] + | ("!=" > relational_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + | ("<>" > relational_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + | ("=~" > regular_expression ) [px::bind(&expr::regex_matches, _val, _1)] + | ("!~" > regular_expression ) [px::bind(&expr::regex_doesnt_match, _val, _1)] ); equality_expression.name("bool expression"); // Evaluate a boolean expression stored as expr into a boolean value. // Throw if the equality_expression does not produce a expr of boolean type. - bool_expr_eval = conditional_expression(_r1) [ px::bind(&expr::evaluate_boolean, _1, _val) ]; + bool_expr_eval = conditional_expression(_r1) [ px::bind(&expr::evaluate_boolean, _1, _val) ]; bool_expr_eval.name("bool_expr_eval"); relational_expression = additive_expression(_r1) [_val = _1] - >> *( ("<=" > additive_expression(_r1) ) [px::bind(&expr::leq, _val, _1)] - | (">=" > additive_expression(_r1) ) [px::bind(&expr::geq, _val, _1)] - | (lit('<') > additive_expression(_r1) ) [px::bind(&expr::lower, _val, _1)] - | (lit('>') > additive_expression(_r1) ) [px::bind(&expr::greater, _val, _1)] + >> *( ("<=" > additive_expression(_r1) ) [px::bind(&expr::leq, _val, _1)] + | (">=" > additive_expression(_r1) ) [px::bind(&expr::geq, _val, _1)] + | (lit('<') > additive_expression(_r1) ) [px::bind(&expr::lower, _val, _1)] + | (lit('>') > additive_expression(_r1) ) [px::bind(&expr::greater, _val, _1)] ); relational_expression.name("relational_expression"); @@ -2058,80 +1978,80 @@ namespace client assignment_statement = variable_reference(_r1)[_a = _1] >> '=' > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. - initializer_list(_r1)[px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] + initializer_list(_r1)[px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. - | eps(px::bind(&MyContext::is_vector_variable_reference, _a)) >> - variable_reference(_r1)[px::bind(&MyContext::copy_vector_variable_to_vector_variable, _r1, _a, _1)] + | eps(px::bind(&MyContext::is_vector_variable_reference, _a)) >> + variable_reference(_r1)[px::bind(&MyContext::copy_vector_variable_to_vector_variable, _r1, _a, _1)] // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. | conditional_expression(_r1) - [px::bind(&MyContext::scalar_variable_assign_scalar_expression, _r1, _a, _1)] + [px::bind(&MyContext::scalar_variable_assign_scalar_expression, _r1, _a, _1)] | (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") - [px::bind(&MyContext::vector_variable_assign_array, _r1, _a, _1, _2)] + [px::bind(&MyContext::vector_variable_assign_array, _r1, _a, _1, _2)] ); new_variable_statement = - (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable, _r1, _a, _1, _b)] > lit('=') > + (kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable, _r1, _a, _1, _b)] > lit('=') > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. - initializer_list(_r1)[px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)] + initializer_list(_r1)[px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. // Only process such variable references, which return a naked vector variable. - | eps(px::bind(&MyContext::could_be_vector_variable_reference, _b)) >> - variable_reference(_r1)[px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)] + | eps(px::bind(&MyContext::could_be_vector_variable_reference, _b)) >> + variable_reference(_r1)[px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)] // Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above. | conditional_expression(_r1) - [px::bind(&MyContext::scalar_variable_new_from_scalar_expression, _r1, _a, _b, _1)] + [px::bind(&MyContext::scalar_variable_new_from_scalar_expression, _r1, _a, _b, _1)] | (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")") - [px::bind(&MyContext::vector_variable_new_from_array, _r1, _a, _b, _1, _2)] + [px::bind(&MyContext::vector_variable_new_from_array, _r1, _a, _b, _1, _2)] ); initializer_list = lit('(') > ( lit(')') | - ( conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)] > - *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]) > + ( conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)] > + *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append, _val, _1)]) > lit(')') ) ); - unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( - variable_reference(_r1) [px::bind(&MyContext::variable_value, _r1, _1, _val)] - | (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] - | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] - | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] - | ((kw["not"] | '!') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] + unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( + variable_reference(_r1) [px::bind(&MyContext::variable_value, _r1, _1, _val)] + | (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] + | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | ((kw["not"] | '!') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] | (kw["min"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') - [ px::bind(&expr::min, _val, _2) ] + [ px::bind(&expr::min, _val, _2) ] | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') - [ px::bind(&expr::max, _val, _2) ] + [ px::bind(&expr::max, _val, _2) ] | (kw["random"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') - [ px::bind(&MyContext::random, _r1, _val, _2) ] + [ px::bind(&MyContext::random, _r1, _val, _2) ] | (kw["digits"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > optional_parameter(_r1)) - [ px::bind(&expr::template digits, _val, _2, _3) ] + [ px::bind(&expr::digits, _val, _2, _3) ] | (kw["zdigits"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > optional_parameter(_r1)) - [ px::bind(&expr::template digits, _val, _2, _3) ] - | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] - | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] - | (kw["is_nil"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_nil_test, _r1, _1, _val)] + [ px::bind(&expr::digits, _val, _2, _3) ] + | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] + | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] + | (kw["is_nil"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_nil_test, _r1, _1, _val)] | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] - | (kw["empty"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_vector_empty, _r1, _1, _val)] - | (kw["size"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::vector_size, _r1, _1, _val)] + | (kw["empty"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_vector_empty, _r1, _1, _val)] + | (kw["size"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::vector_size, _r1, _1, _val)] | (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ] - | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _r1, _1, _2, _val) ] - | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _r1, _1, _2, _val) ] - | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _r1, _1, _2, _val) ] + | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _r1, _1, _2, _val) ] + | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _r1, _1, _2, _val) ] + | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _r1, _1, _2, _val) ] | raw[lexeme['"' > *((utf8char - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] - [ px::bind(&FactorActions::string_, _r1, _1, _val) ] + [ px::bind(&FactorActions::string_, _r1, _1, _val) ] ); unary_expression.name("unary_expression"); one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2]; one_of.name("one_of"); one_of_list = - eps[px::bind(&expr::one_of_test_init, _val)] > + eps[px::bind(&expr::one_of_test_init, _val)] > ( ( ',' > *( ( - unary_expression(_r1)[px::bind(&expr::template one_of_test, _r2, _1, _val)] - | (lit('~') > unary_expression(_r1))[px::bind(&expr::template one_of_test, _r2, _1, _val)] - | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] + unary_expression(_r1)[px::bind(&expr::one_of_test, _r2, _1, _val)] + | (lit('~') > unary_expression(_r1))[px::bind(&expr::one_of_test, _r2, _1, _val)] + | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] ) >> -lit(',')) ) | eps @@ -2139,16 +2059,16 @@ namespace client one_of_list.name("one_of_list"); interpolate_table = (unary_expression(_r1)[_a = _1] > ',' > interpolate_table_list(_r1, _a)) - [px::bind(&InterpolateTableContext::evaluate, _a, _2, _val)]; + [px::bind(&InterpolateTableContext::evaluate, _a, _2, _val)]; interpolate_table.name("interpolate_table"); interpolate_table_list = - eps[px::bind(&InterpolateTableContext::init, _r2)] > + eps[px::bind(&InterpolateTableContext::init, _r2)] > ( *(( lit('(') > unary_expression(_r1) > ',' > unary_expression(_r1) > ')' ) - [px::bind(&InterpolateTableContext::add_pair, _1, _2, _val)] >> -lit(',')) ); + [px::bind(&InterpolateTableContext::add_pair, _1, _2, _val)] >> -lit(',')) ); interpolate_table.name("interpolate_table_list"); - optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( - lit(')') [ px::bind(&FactorActions::noexpr, _val) ] + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( + lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] ); optional_parameter.name("optional_parameter"); @@ -2156,13 +2076,13 @@ namespace client variable_reference = variable(_r1)[_a=_1] >> ( - ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > iter_pos) - [px::bind(&MyContext::store_variable_index, _r1, _a, _b, _2, _val)] + ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > iter_pos) + [px::bind(&MyContext::store_variable_index, _r1, _a, _b, _2, _val)] | eps[_val=_a] ); variable_reference.name("variable reference"); - variable = identifier[ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + variable = identifier[ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; variable.name("variable name"); regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']]; @@ -2225,8 +2145,8 @@ namespace client } } - // Generic expression over expr. - typedef qi::rule(const MyContext*), spirit_encoding::space_type> RuleExpression; + // Generic expression over expr. + typedef qi::rule RuleExpression; // The start of the grammar. qi::rule, spirit_encoding::space_type> start; @@ -2239,9 +2159,9 @@ namespace client // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. qi::rule legacy_variable_expansion; // Parsed identifier name. - qi::rule(), spirit_encoding::space_type> identifier; + qi::rule identifier; // Ternary operator (?:) over logical_or_expression. - qi::rule(const MyContext*), qi::locals, spirit_encoding::space_type> conditional_expression; + qi::rule, spirit_encoding::space_type> conditional_expression; // Logical or over logical_and_expressions. RuleExpression logical_or_expression; // Logical and over relational_expressions. @@ -2259,51 +2179,39 @@ namespace client // Accepting an optional parameter. RuleExpression optional_parameter; // Rule to capture a regular expression enclosed in //. - qi::rule(), spirit_encoding::space_type> regular_expression; + qi::rule regular_expression; // Evaluate boolean expression into bool. qi::rule bool_expr_eval; // Reference of a scalar variable, or reference to a field of a vector variable. - qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> variable_reference; + qi::rule, spirit_encoding::space_type> variable_reference; // Rule to translate an identifier to a ConfigOption, or to fail. - qi::rule(const MyContext*), spirit_encoding::space_type> variable; + qi::rule variable; // Evaluating whether a nullable variable is nil. - qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; + qi::rule is_nil_test; // Evaluating "one of" list of patterns. - qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> one_of; - qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list; + qi::rule, spirit_encoding::space_type> one_of; + qi::rule one_of_list; // Evaluating the "interpolate_table" expression. - qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> interpolate_table; - qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> interpolate_table_list; + qi::rule, spirit_encoding::space_type> interpolate_table; + qi::rule interpolate_table_list; qi::rule, spirit_encoding::space_type> if_else_output; - qi::rule>, spirit_encoding::space_type> assignment_statement; + qi::rule, spirit_encoding::space_type> assignment_statement; // Allocating new local or global variables. - qi::rule>, spirit_encoding::space_type> new_variable_statement; - qi::rule>(const MyContext*), spirit_encoding::space_type> initializer_list; + qi::rule, spirit_encoding::space_type> new_variable_statement; + qi::rule(const MyContext*), spirit_encoding::space_type> initializer_list; -// qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; qi::symbols keywords; }; } +static const client::macro_processor g_macro_processor_instance; + static std::string process_macro(const std::string &templ, client::MyContext &context) { - typedef std::string::const_iterator iterator_type; - typedef client::macro_processor macro_processor; - - // Our whitespace skipper. - spirit_encoding::space_type space; - // Our grammar, statically allocated inside the method, meaning it will be allocated the first time - // PlaceholderParser::process() runs. - //FIXME this kind of initialization is not thread safe! - static macro_processor macro_processor_instance; - // Iterators over the source template. - std::string::const_iterator iter = templ.begin(); - std::string::const_iterator end = templ.end(); - // Accumulator for the processed template. - std::string output; - phrase_parse(iter, end, macro_processor_instance(&context), space, output); - if (!context.error_message.empty()) { + std::string output; + phrase_parse(templ.begin(), templ.end(), g_macro_processor_instance(&context), spirit_encoding::space_type{}, output); + if (! context.error_message.empty()) { if (context.error_message.back() != '\n' && context.error_message.back() != '\r') context.error_message += '\n'; throw Slic3r::PlaceholderParserError(context.error_message); From a53070f5e61cbe5539775cf94fa2de4fb444f87c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 24 Mar 2023 16:35:53 +0100 Subject: [PATCH 165/201] PlaceholderParser: Simpler if / elsif / else / endif syntax. {if cond1}{expr1}{elsif cond2}{expr2}{else}{expr3}{endif} could be written as {if cond1;expr1;elsif cond2;expr2;else;expr3;endif} the first semicolon after else is not manadtory, thus the following is valid {if cond1;expr1;elsif cond2;expr2;else expr3;endif} each expression could be also empty or contain multiple expressions: {if cond1;elsif cond2;else endif} --- src/libslic3r/PlaceholderParser.cpp | 24 ++++++++++++------- tests/libslic3r/test_placeholder_parser.cpp | 26 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index fb9fdbea0..db7c95bfd 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1862,7 +1862,7 @@ namespace client // Allow back tracking after '{' in case of a text_block embedded inside a condition. // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. - | (lit('{') >> macro(_r1)[_val+=_1] > *(+lit(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}') + | (lit('{') >> macro(_r1)[_val += _1] % (+lit(';')) > *lit(';') > '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -1884,16 +1884,24 @@ namespace client macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). + // Also }{ could be replaced with ; to simplify writing of pure code. if_else_output = eps[_a=true] > - (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > '}' > - text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{' > - *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > '}' > - text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{') > - -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > lit('}') > - text_block(_r1)[px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > '{') > + (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > (if_text_block(_r1) | if_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > + *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > (if_text_block(_r1) | if_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)]) > + -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > (if_text_block(_r1) | else_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > kw["endif"]; if_else_output.name("if_else_output"); + if_text_block = (lit('}') > text_block(_r1) > '{'); + if_text_block.name("if_text_block"); + if_macros = +lit(';') > (macro(_r1)[_val += _1] % (+lit(';')) | eps) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])); + if_macros.name("if_macros"); + else_macros = *lit(';') > (macro(_r1)[_val += _1] % (+lit(';')) | eps) > (+lit(';') | &kw["endif"]); + else_macros.name("else_macros"); + // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). /* switch_output = @@ -2155,7 +2163,7 @@ namespace client // A free-form text, possibly empty, possibly containing macro expansions. qi::rule text_block; // Statements enclosed in curely braces {} - qi::rule macro; + qi::rule macro, if_text_block, if_macros, else_macros; // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. qi::rule legacy_variable_expansion; // Parsed identifier name. diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 0e3521a23..79cdfdea9 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -245,10 +245,36 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } + SECTION("nested if with new variables 2, mixing }{ with ;") { + std::string script = + "{if 1 == 0;local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{size(myfloats)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); + } SECTION("nested if with new variables, two level") { std::string script = "{if 1 == 1}{if 2 == 3}{nejaka / haluz}{else}{local myints = (6, 5, 4, 3, 2, 1)}{endif}{else}{if zase * haluz}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}{endif}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } + SECTION("if with empty block and ;") { + std::string script = + "{if false;else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{size(myfloats)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); + } + SECTION("nested if with new variables, two level, mixing }{ with ;") { + std::string script = + "{if 1 == 1;if 2 == 3;nejaka / haluz;else;local myints = (6, 5, 4, 3, 2, 1);endif;else;if zase * haluz;else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif;endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("nested if with new variables, two level, ;, no semicolon after else") { + std::string script = + "{if 1 == 1;if 2 == 3;nejaka / haluz;else local myints = (6, 5, 4, 3, 2, 1);endif;else if zase * haluz;else local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif;endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("if else completely empty") { REQUIRE(parser.process("{if false; elsif false; else; endif}", 0, nullptr, nullptr, nullptr) == ""); } + SECTION("if else completely empty 2") { REQUIRE(parser.process("{if false; elsif false; else endif}", 0, nullptr, nullptr, nullptr) == ""); } } From 8e0b45fc839008a76c5d50ae42f85766fc843259 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 27 Mar 2023 10:44:54 +0200 Subject: [PATCH 166/201] Fix compilation - add missing includes --- src/libslic3r/ExtrusionRole.cpp | 1 + src/libslic3r/Format/SLAArchiveReader.cpp | 1 + src/libslic3r/Preset.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 1 + 4 files changed, 4 insertions(+) diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp index 01ec73ed1..a7ec31949 100644 --- a/src/libslic3r/ExtrusionRole.cpp +++ b/src/libslic3r/ExtrusionRole.cpp @@ -1,4 +1,5 @@ #include "ExtrusionRole.hpp" +#include "I18N.hpp" #include #include diff --git a/src/libslic3r/Format/SLAArchiveReader.cpp b/src/libslic3r/Format/SLAArchiveReader.cpp index f80b9a2ba..7d549216c 100644 --- a/src/libslic3r/Format/SLAArchiveReader.cpp +++ b/src/libslic3r/Format/SLAArchiveReader.cpp @@ -1,6 +1,7 @@ #include "SLAArchiveReader.hpp" #include "SL1.hpp" #include "SL1_SVG.hpp" +#include "I18N.hpp" #include "libslic3r/SlicesToTriangleMesh.hpp" diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a58492062..464bb7daa 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -3,6 +3,7 @@ #include "Exception.hpp" #include "Preset.hpp" #include "AppConfig.hpp" +#include "I18N.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 44c7f40ab..1fd718a25 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -13,6 +13,7 @@ #include #include +#include namespace Slic3r::GUI { From c654a6714a03b2739dc82d2079b3b4a0227a5ba9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 27 Mar 2023 12:24:07 +0200 Subject: [PATCH 167/201] Fixed validation of cfg when a vector contains a nullable option which is nil for some elements but not for others #10163, SPE-1613 --- src/libslic3r/PrintConfig.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0089ce457..60717b68c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4552,12 +4552,19 @@ std::string validate(const FullPrintConfig &cfg) } case coFloats: case coPercents: - for (double v : static_cast*>(opt)->values) + { + const auto* vec = static_cast*>(opt); + for (size_t i = 0; i < vec->size(); ++i) { + if (vec->is_nil(i)) + continue; + double v = vec->values[i]; if (v < optdef->min || v > optdef->max) { out_of_range = true; break; } + } break; + } case coInt: { auto *iopt = static_cast(opt); @@ -4565,12 +4572,19 @@ std::string validate(const FullPrintConfig &cfg) break; } case coInts: - for (int v : static_cast*>(opt)->values) + { + const auto* vec = static_cast*>(opt); + for (size_t i = 0; i < vec->size(); ++i) { + if (vec->is_nil(i)) + continue; + int v = vec->values[i]; if (v < optdef->min || v > optdef->max) { out_of_range = true; break; } + } break; + } default:; } if (out_of_range) From 0eb2a2cf0479eb50075c8db41883d30f2d3a6f98 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 27 Mar 2023 13:26:57 +0200 Subject: [PATCH 168/201] Disable "infill only where needed" option Note: only commented out for now. --- src/libslic3r/LayerRegion.cpp | 2 +- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 18 ++-- src/libslic3r/PrintConfig.hpp | 2 +- src/libslic3r/PrintObject.cpp | 184 +++++++++++++++++----------------- src/slic3r/GUI/Tab.cpp | 2 +- tests/fff_print/test_fill.cpp | 148 +++++++++++++-------------- 7 files changed, 180 insertions(+), 178 deletions(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6374766cd..d722f1e9c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -727,7 +727,7 @@ void LayerRegion::prepare_fill_surfaces() if (! spiral_vase && this->region().config().top_solid_layers == 0) { for (Surface &surface : m_fill_surfaces) if (surface.is_top()) - surface.surface_type = this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid : stInternal; + surface.surface_type = /*this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid :*/ stInternal; } if (this->region().config().bottom_solid_layers == 0) { for (Surface &surface : m_fill_surfaces) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 464bb7daa..23f1438c4 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -425,7 +425,7 @@ static std::vector s_Preset_print_options { "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "infill_every_layers", /*"infill_only_where_needed",*/ "solid_infill_every_layers", "fill_angle", "bridge_angle", "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 60717b68c..2fb2b877a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1560,14 +1560,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); - def = this->add("infill_only_where_needed", coBool); - def->label = L("Only infill where needed"); - def->category = L("Infill"); - def->tooltip = L("This option will limit infill to the areas actually needed for supporting ceilings " - "(it will act as internal support material). If enabled, slows down the G-code generation " - "due to the multiple checks involved."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); + // def = this->add("infill_only_where_needed", coBool); + // def->label = L("Only infill where needed"); + // def->category = L("Infill"); + // def->tooltip = L("This option will limit infill to the areas actually needed for supporting ceilings " + // "(it will act as internal support material). If enabled, slows down the G-code generation " + // "due to the multiple checks involved."); + // def->mode = comAdvanced; + // def->set_default_value(new ConfigOptionBool(false)); def = this->add("infill_overlap", coFloatOrPercent); def->label = L("Infill/perimeters overlap"); @@ -4154,6 +4154,8 @@ static std::set PrintConfigDef_ignore = { "wall_add_middle_threshold", "wall_split_middle_threshold", // Replaced by new concentric ensuring in 2.6.0-alpha5 "ensure_vertical_shell_thickness", + // Disabled in 2.6.0-alpha6, this option is problematic + "infill_only_where_needed", }; void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d29afe5e7..08f3bcd2f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -495,7 +495,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, extrusion_width)) ((ConfigOptionFloat, first_layer_acceleration_over_raft)) ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) - ((ConfigOptionBool, infill_only_where_needed)) + // ((ConfigOptionBool, infill_only_where_needed)) // Force the generation of solid shells between adjacent materials/volumes. ((ConfigOptionBool, interface_shells)) ((ConfigOptionFloat, layer_height)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7136617d8..493bb9070 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -348,8 +348,8 @@ void PrintObject::prepare_infill() //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. - this->clip_fill_surfaces(); - m_print->throw_if_canceled(); + // this->clip_fill_surfaces(); + // m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { @@ -2419,98 +2419,98 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // Also one wishes the perimeters to be supported by a full infill. // Idempotence of this method is guaranteed by the fact that we don't remove things from // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. -void PrintObject::clip_fill_surfaces() -{ - bool has_lightning_infill = false; - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) - if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) - has_lightning_infill = true; +// void PrintObject::clip_fill_surfaces() +// { +// bool has_lightning_infill = false; +// for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) +// if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) +// has_lightning_infill = true; - // For Lightning infill, infill_only_where_needed is ignored because both - // do a similar thing, and their combination doesn't make much sense. - if (! m_config.infill_only_where_needed.value || has_lightning_infill) - return; - bool has_infill = false; - for (size_t i = 0; i < this->num_printing_regions(); ++ i) - if (this->printing_region(i).config().fill_density > 0) { - has_infill = true; - break; - } - if (! has_infill) - return; +// // For Lightning infill, infill_only_where_needed is ignored because both +// // do a similar thing, and their combination doesn't make much sense. +// if (! m_config.infill_only_where_needed.value || has_lightning_infill) +// return; +// bool has_infill = false; +// for (size_t i = 0; i < this->num_printing_regions(); ++ i) +// if (this->printing_region(i).config().fill_density > 0) { +// has_infill = true; +// break; +// } +// if (! has_infill) +// return; - // We only want infill under ceilings; this is almost like an - // internal support material. - // Proceed top-down, skipping the bottom layer. - Polygons upper_internal; - for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { - Layer *layer = m_layers[layer_id]; - Layer *lower_layer = m_layers[layer_id - 1]; - // Detect things that we need to support. - // Cummulative fill surfaces. - Polygons fill_surfaces; - // Solid surfaces to be supported. - Polygons overhangs; - for (const LayerRegion *layerm : layer->m_regions) - for (const Surface &surface : layerm->fill_surfaces()) { - Polygons polygons = to_polygons(surface.expolygon); - if (surface.is_solid()) - polygons_append(overhangs, polygons); - polygons_append(fill_surfaces, std::move(polygons)); - } - Polygons lower_layer_fill_surfaces; - Polygons lower_layer_internal_surfaces; - for (const LayerRegion *layerm : lower_layer->m_regions) - for (const Surface &surface : layerm->fill_surfaces()) { - Polygons polygons = to_polygons(surface.expolygon); - if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) - polygons_append(lower_layer_internal_surfaces, polygons); - polygons_append(lower_layer_fill_surfaces, std::move(polygons)); - } - // We also need to support perimeters when there's at least one full unsupported loop - { - // Get perimeters area as the difference between slices and fill_surfaces - // Only consider the area that is not supported by lower perimeters - Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces); - // Only consider perimeter areas that are at least one extrusion width thick. - //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. - //Should the pw not be half of the current value? - float pw = FLT_MAX; - for (const LayerRegion *layerm : layer->m_regions) - pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width()); - // Append such thick perimeters to the areas that need support - polygons_append(overhangs, opening(perimeters, pw)); - } - // Merge the new overhangs, find new internal infill. - polygons_append(upper_internal, std::move(overhangs)); - static constexpr const auto closing_radius = scaled(2.f); - upper_internal = intersection( - // Regularize the overhang regions, so that the infill areas will not become excessively jagged. - smooth_outward( - closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), - scaled(0.1)), - lower_layer_internal_surfaces); - // Apply new internal infill to regions. - for (LayerRegion *layerm : lower_layer->m_regions) { - if (layerm->region().config().fill_density.value == 0) - continue; - Polygons internal; - for (Surface &surface : layerm->m_fill_surfaces.surfaces) - if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) - polygons_append(internal, std::move(surface.expolygon)); - layerm->m_fill_surfaces.remove_types({ stInternal, stInternalVoid }); - layerm->m_fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal); - layerm->m_fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid); - // If there are voids it means that our internal infill is not adjacent to - // perimeters. In this case it would be nice to add a loop around infill to - // make it more robust and nicer. TODO. -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); -#endif - } - m_print->throw_if_canceled(); - } -} // void PrintObject::clip_fill_surfaces() +// // We only want infill under ceilings; this is almost like an +// // internal support material. +// // Proceed top-down, skipping the bottom layer. +// Polygons upper_internal; +// for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { +// Layer *layer = m_layers[layer_id]; +// Layer *lower_layer = m_layers[layer_id - 1]; +// // Detect things that we need to support. +// // Cummulative fill surfaces. +// Polygons fill_surfaces; +// // Solid surfaces to be supported. +// Polygons overhangs; +// for (const LayerRegion *layerm : layer->m_regions) +// for (const Surface &surface : layerm->fill_surfaces()) { +// Polygons polygons = to_polygons(surface.expolygon); +// if (surface.is_solid()) +// polygons_append(overhangs, polygons); +// polygons_append(fill_surfaces, std::move(polygons)); +// } +// Polygons lower_layer_fill_surfaces; +// Polygons lower_layer_internal_surfaces; +// for (const LayerRegion *layerm : lower_layer->m_regions) +// for (const Surface &surface : layerm->fill_surfaces()) { +// Polygons polygons = to_polygons(surface.expolygon); +// if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) +// polygons_append(lower_layer_internal_surfaces, polygons); +// polygons_append(lower_layer_fill_surfaces, std::move(polygons)); +// } +// // We also need to support perimeters when there's at least one full unsupported loop +// { +// // Get perimeters area as the difference between slices and fill_surfaces +// // Only consider the area that is not supported by lower perimeters +// Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces); +// // Only consider perimeter areas that are at least one extrusion width thick. +// //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. +// //Should the pw not be half of the current value? +// float pw = FLT_MAX; +// for (const LayerRegion *layerm : layer->m_regions) +// pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width()); +// // Append such thick perimeters to the areas that need support +// polygons_append(overhangs, opening(perimeters, pw)); +// } +// // Merge the new overhangs, find new internal infill. +// polygons_append(upper_internal, std::move(overhangs)); +// static constexpr const auto closing_radius = scaled(2.f); +// upper_internal = intersection( +// // Regularize the overhang regions, so that the infill areas will not become excessively jagged. +// smooth_outward( +// closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), +// scaled(0.1)), +// lower_layer_internal_surfaces); +// // Apply new internal infill to regions. +// for (LayerRegion *layerm : lower_layer->m_regions) { +// if (layerm->region().config().fill_density.value == 0) +// continue; +// Polygons internal; +// for (Surface &surface : layerm->m_fill_surfaces.surfaces) +// if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) +// polygons_append(internal, std::move(surface.expolygon)); +// layerm->m_fill_surfaces.remove_types({ stInternal, stInternalVoid }); +// layerm->m_fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal); +// layerm->m_fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid); +// // If there are voids it means that our internal infill is not adjacent to +// // perimeters. In this case it would be nice to add a loop around infill to +// // make it more robust and nicer. TODO. +// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING +// layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); +// #endif +// } +// m_print->throw_if_canceled(); +// } +// } // void PrintObject::clip_fill_surfaces() void PrintObject::discover_horizontal_shells() { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b095d4ea2..1b2a1ba81 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1469,7 +1469,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Reducing printing time")); category_path = "infill_42#"; optgroup->append_single_option_line("infill_every_layers", category_path + "combine-infill-every-x-layers"); - optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed"); + // optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed"); optgroup = page->new_optgroup(L("Advanced")); optgroup->append_single_option_line("solid_infill_every_layers", category_path + "solid-infill-every-x-layers"); diff --git a/tests/fff_print/test_fill.cpp b/tests/fff_print/test_fill.cpp index 8f024102d..6d8422c82 100644 --- a/tests/fff_print/test_fill.cpp +++ b/tests/fff_print/test_fill.cpp @@ -252,85 +252,85 @@ SCENARIO("Infill does not exceed perimeters", "[Fill]") GIVEN("Concentric") { test("concentric"sv); } } -SCENARIO("Infill only where needed", "[Fill]") -{ - DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); - config.set_deserialize_strict({ - { "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" }, - { "infill_only_where_needed", true }, - { "bottom_solid_layers", 0 }, - { "infill_extruder", 2 }, - { "infill_extrusion_width", 0.5 }, - { "wipe_into_infill", false }, - { "fill_density", 0.4 }, - // for preventing speeds from being altered - { "cooling", "0, 0, 0, 0" }, - // for preventing speeds from being altered - { "first_layer_speed", "100%" } - }); +// SCENARIO("Infill only where needed", "[Fill]") +// { +// DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); +// config.set_deserialize_strict({ +// { "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" }, +// { "infill_only_where_needed", true }, +// { "bottom_solid_layers", 0 }, +// { "infill_extruder", 2 }, +// { "infill_extrusion_width", 0.5 }, +// { "wipe_into_infill", false }, +// { "fill_density", 0.4 }, +// // for preventing speeds from being altered +// { "cooling", "0, 0, 0, 0" }, +// // for preventing speeds from being altered +// { "first_layer_speed", "100%" } +// }); - auto test = [&config]() -> double { - TriangleMesh pyramid = Test::mesh(Slic3r::Test::TestMesh::pyramid); - // Arachne doesn't use "Detect thin walls," and because of this, it filters out tiny infill areas differently. - // So, for Arachne, we cut the pyramid model to achieve similar results. - if (config.opt_enum("perimeter_generator") == Slic3r::PerimeterGeneratorType::Arachne) { - indexed_triangle_set lower{}; - cut_mesh(pyramid.its, 35, nullptr, &lower); - pyramid = TriangleMesh(lower); - } - std::string gcode = Slic3r::Test::slice({ pyramid }, config); - THEN("gcode not empty") { - REQUIRE(! gcode.empty()); - } +// auto test = [&config]() -> double { +// TriangleMesh pyramid = Test::mesh(Slic3r::Test::TestMesh::pyramid); +// // Arachne doesn't use "Detect thin walls," and because of this, it filters out tiny infill areas differently. +// // So, for Arachne, we cut the pyramid model to achieve similar results. +// if (config.opt_enum("perimeter_generator") == Slic3r::PerimeterGeneratorType::Arachne) { +// indexed_triangle_set lower{}; +// cut_mesh(pyramid.its, 35, nullptr, &lower); +// pyramid = TriangleMesh(lower); +// } +// std::string gcode = Slic3r::Test::slice({ pyramid }, config); +// THEN("gcode not empty") { +// REQUIRE(! gcode.empty()); +// } - GCodeReader parser; - int tool = -1; - const int infill_extruder = config.opt_int("infill_extruder"); - Points infill_points; - parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) - { - // if the command is a T command, set the the current tool - if (boost::starts_with(line.cmd(), "T")) { - tool = atoi(line.cmd().data() + 1) + 1; - } else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) { - if (tool == infill_extruder) { - infill_points.emplace_back(self.xy_scaled()); - infill_points.emplace_back(line.new_XY_scaled(self)); - } - } - }); - // prevent calling convex_hull() with no points - THEN("infill not empty") { - REQUIRE(! infill_points.empty()); - } +// GCodeReader parser; +// int tool = -1; +// const int infill_extruder = config.opt_int("infill_extruder"); +// Points infill_points; +// parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) +// { +// // if the command is a T command, set the the current tool +// if (boost::starts_with(line.cmd(), "T")) { +// tool = atoi(line.cmd().data() + 1) + 1; +// } else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) { +// if (tool == infill_extruder) { +// infill_points.emplace_back(self.xy_scaled()); +// infill_points.emplace_back(line.new_XY_scaled(self)); +// } +// } +// }); +// // prevent calling convex_hull() with no points +// THEN("infill not empty") { +// REQUIRE(! infill_points.empty()); +// } - auto opt_width = config.opt("infill_extrusion_width"); - REQUIRE(! opt_width->percent); - Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled(opt_width->value / 2)); - return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); }); - }; +// auto opt_width = config.opt("infill_extrusion_width"); +// REQUIRE(! opt_width->percent); +// Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled(opt_width->value / 2)); +// return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); }); +// }; - double tolerance = 5; // mm^2 +// double tolerance = 5; // mm^2 - // GIVEN("solid_infill_below_area == 0") { - // config.opt_float("solid_infill_below_area") = 0; - // WHEN("pyramid is sliced ") { - // auto area = test(); - // THEN("no infill is generated when using infill_only_where_needed on a pyramid") { - // REQUIRE(area < tolerance); - // } - // } - // } - // GIVEN("solid_infill_below_area == 70") { - // config.opt_float("solid_infill_below_area") = 70; - // WHEN("pyramid is sliced ") { - // auto area = test(); - // THEN("infill is only generated under the forced solid shells") { - // REQUIRE(std::abs(area - 70) < tolerance); - // } - // } - // } -} +// // GIVEN("solid_infill_below_area == 0") { +// // config.opt_float("solid_infill_below_area") = 0; +// // WHEN("pyramid is sliced ") { +// // auto area = test(); +// // THEN("no infill is generated when using infill_only_where_needed on a pyramid") { +// // REQUIRE(area < tolerance); +// // } +// // } +// // } +// // GIVEN("solid_infill_below_area == 70") { +// // config.opt_float("solid_infill_below_area") = 70; +// // WHEN("pyramid is sliced ") { +// // auto area = test(); +// // THEN("infill is only generated under the forced solid shells") { +// // REQUIRE(std::abs(area - 70) < tolerance); +// // } +// // } +// // } +// } SCENARIO("Combine infill", "[Fill]") { From 3ad5f52b00c97cb229fd1e1cfda6fe3fb51fc6a6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Mar 2023 13:34:21 +0200 Subject: [PATCH 169/201] Simplified "Save/Rename Preset" dialog when only one preset is saved/renamed. --- src/slic3r/GUI/SavePresetDialog.cpp | 57 +++++++++++-------------- src/slic3r/GUI/SavePresetDialog.hpp | 8 ++-- src/slic3r/GUI/Tab.cpp | 4 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 2 +- 4 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 33f41b9c4..09ae10207 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -85,18 +85,17 @@ void SavePresetDialog::Item::init_input_name_ctrl(wxBoxSizer *input_name_sizer, } } -wxString SavePresetDialog::Item::get_top_label_text() const +static std::map TOP_LABELS = { - const std::string label_str = m_use_text_ctrl ? - // TRN %1% = "Preset" - L("Rename %1% to") : - // TRN %1% = "Preset" - L("Save %1% as"); - Tab* tab = wxGetApp().get_tab(m_type); - return format_wxstr(_(label_str) + ":", tab->title()); -} + // type Save settings + { Preset::Type::TYPE_PRINT, L("Save print settings as") }, + { Preset::Type::TYPE_SLA_PRINT, L("Save print settings as") }, + { Preset::Type::TYPE_FILAMENT, L("Save filament settings as")}, + { Preset::Type::TYPE_SLA_MATERIAL, L("Save material settings as")}, + { Preset::Type::TYPE_PRINTER, L("Save printer settings as") }, +}; -SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent): +SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent, bool is_for_multiple_save): m_type(type), m_use_text_ctrl(parent->is_for_rename()), m_parent(parent), @@ -105,14 +104,15 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox { m_valid_label->SetFont(wxGetApp().bold_font()); - wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, get_top_label_text()); + wxStaticText* label_top = is_for_multiple_save ? new wxStaticText(m_parent, wxID_ANY, _(TOP_LABELS.at(m_type)) + ":") : nullptr; wxBoxSizer* input_name_sizer = new wxBoxSizer(wxHORIZONTAL); input_name_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W); init_input_name_ctrl(input_name_sizer, get_init_preset_name(suffix)); - sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); - sizer->Add(input_name_sizer,0, wxEXPAND | wxBOTTOM, BORDER_W); + if (label_top) + sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); + sizer->Add(input_name_sizer,0, wxEXPAND | (label_top ? 0 : wxTOP) | wxBOTTOM, BORDER_W); sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W); if (m_type == Preset::TYPE_PRINTER) @@ -205,8 +205,6 @@ void SavePresetDialog::Item::update() if ((!m_use_text_ctrl && m_presets->get_edited_preset().is_dirty) || (dlg && dlg->get_preset_bundle())) // means that we save modifications from the DiffDialog info_line = _L("Save preset modifications to existing user profile"); - else - info_line = _L("Nothing changed"); m_valid_type = ValidationType::Valid; } else { @@ -266,8 +264,8 @@ void SavePresetDialog::Item::update() void SavePresetDialog::Item::update_valid_bmp() { - std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation" : - m_valid_type == ValidationType::NoValid ? "cross" : "tick_mark" ; + std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation_manifold" : + m_valid_type == ValidationType::NoValid ? "exclamation" : "tick_mark" ; m_valid_bmp->SetBitmap(*get_bmp_bundle(bmp_name)); } @@ -289,22 +287,17 @@ void SavePresetDialog::Item::Enable(bool enable /*= true*/) // SavePresetDialog //----------------------------------------------- -SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix, bool template_filament) - : DPIDialog(parent, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) -{ - build(std::vector{type}, suffix, template_filament); -} - SavePresetDialog::SavePresetDialog(wxWindow* parent, std::vector types, std::string suffix, bool template_filament/* =false*/, PresetBundle* preset_bundle/* = nullptr*/) - : DPIDialog(parent, wxID_ANY, _L("Save presets"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), + : DPIDialog(parent, wxID_ANY, types.size() == 1 ? _L("Save preset") : _L("Save presets"), + wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING), m_preset_bundle(preset_bundle) { build(types, suffix, template_filament); } -SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention) - : DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), - m_use_for_rename(rename), +SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, const wxString& info_line_extention) + : DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING), + m_use_for_rename(true), m_info_line_extention(info_line_extention) { build(std::vector{type}); @@ -335,9 +328,9 @@ void SavePresetDialog::build(std::vector types, std::string suffix m_presets_sizer = new wxBoxSizer(wxVERTICAL); - // Add first item - for (Preset::Type type : types) - AddItem(type, suffix); + const bool is_for_multiple_save = types.size() > 1; + for (const Preset::Type& type : types) + AddItem(type, suffix, is_for_multiple_save); // Add dialog's buttons wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); @@ -367,9 +360,9 @@ void SavePresetDialog::build(std::vector types, std::string suffix #endif } -void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix) +void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix, bool is_for_multiple_save) { - m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this}); + m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this, is_for_multiple_save}); } std::string SavePresetDialog::get_name() diff --git a/src/slic3r/GUI/SavePresetDialog.hpp b/src/slic3r/GUI/SavePresetDialog.hpp index 3088d457f..1450ebbd2 100644 --- a/src/slic3r/GUI/SavePresetDialog.hpp +++ b/src/slic3r/GUI/SavePresetDialog.hpp @@ -36,7 +36,7 @@ public: Warning }; - Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent); + Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent, bool is_for_multiple_save); Item(wxWindow* parent, wxBoxSizer* sizer, const std::string& def_name, PrinterTechnology pt = ptFFF); void update_valid_bmp(); @@ -65,7 +65,6 @@ public: std::string get_init_preset_name(const std::string &suffix); void init_input_name_ctrl(wxBoxSizer *input_name_sizer, std::string preset_name); const Preset* get_existing_preset() const ; - wxString get_top_label_text() const ; void update(); }; @@ -89,12 +88,11 @@ public: const wxString& get_info_line_extention() { return m_info_line_extention; } - SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix = "", bool template_filament = false); SavePresetDialog(wxWindow* parent, std::vector types, std::string suffix = "", bool template_filament = false, PresetBundle* preset_bundle = nullptr); - SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention); + SavePresetDialog(wxWindow* parent, Preset::Type type, const wxString& info_line_extention); ~SavePresetDialog() override; - void AddItem(Preset::Type type, const std::string& suffix); + void AddItem(Preset::Type type, const std::string& suffix, bool is_for_multiple_save); PresetBundle* get_preset_bundle() const { return m_preset_bundle; } std::string get_name(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1b2a1ba81..3fb7bf605 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3820,7 +3820,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) } if (name.empty()) { - SavePresetDialog dlg(m_parent, m_type, detach ? _u8L("Detached") : "", from_template); + SavePresetDialog dlg(m_parent, { m_type }, detach ? _u8L("Detached") : "", from_template); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); @@ -3931,7 +3931,7 @@ void Tab::rename_preset() // get new name - SavePresetDialog dlg(m_parent, m_type, true, msg); + SavePresetDialog dlg(m_parent, m_type, msg); if (dlg.ShowModal() != wxID_OK) return; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index d7d4798c2..ad702c1b3 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1012,7 +1012,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_s // for system/default/external presets we should take an edited name if (preset.is_system || preset.is_default || preset.is_external) { - SavePresetDialog save_dlg(this, preset.type); + SavePresetDialog save_dlg(this, { preset.type }); if (save_dlg.ShowModal() != wxID_OK) { m_exit_action = Action::Discard; return false; From c6c6f361dab23704c9fbc8f0efb00d20ff92aa4e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 27 Mar 2023 14:11:38 +0200 Subject: [PATCH 170/201] remove infill lines which are snapped to the perimeter from anchors - they confuse the angle selection algorithm --- src/libslic3r/PrintObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 493bb9070..61067ed18 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2107,7 +2107,7 @@ void PrintObject::bridge_over_infill() total_fill_area = closing(total_fill_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); - Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); + Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + From f591c2503af8528d38b9f77c2d5a0e6ec27dae21 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 27 Mar 2023 15:36:05 +0200 Subject: [PATCH 171/201] PlaceholderParser: changed the syntax of if inside a {} block to {if condition then block elsif} The "then" keyword is now mandatory. On the other hand, "then" keyword must NOT be used using old syntax: {if condition}...{endif} --- src/libslic3r/PlaceholderParser.cpp | 41 +++++++++++++-------- tests/libslic3r/test_placeholder_parser.cpp | 19 ++++++---- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index db7c95bfd..11ffd4a54 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -177,7 +177,7 @@ namespace client bool writable { false }; // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. int index { -1 }; - IteratorRange it_range; + IteratorRange it_range; bool empty() const { return opt == nullptr; } bool has_index() const { return index != -1; } @@ -1862,7 +1862,7 @@ namespace client // Allow back tracking after '{' in case of a text_block embedded inside a condition. // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. - | (lit('{') >> macro(_r1)[_val += _1] % (+lit(';')) > *lit(';') > '}') + | (lit('{') >> (macros(_r1)[_val += _1] > '}') | '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -1874,14 +1874,29 @@ namespace client // New style of macro expansion. // The macro expansion may contain numeric or string expressions, ifs and cases. - macro = - (kw["if"] > if_else_output(_r1) [_val = _1]) -// | (kw["switch"] > switch_output(_r1) [_val = _1]) - | (assignment_statement(_r1) [_val = _1]) - | (new_variable_statement(_r1) [_val = _1]) - | (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + macros = + +(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &lit('}')))[_val += _1] | +lit(';')); + macros.name("macro"); + // if_macros and else_macros only differ by the look-ahead ending condition, which is to not have to repeat the last semicolon + // at the end of the block. + if_macros = kw["then"] > *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])))[_val += _1] | +lit(';')); + if_macros.name("if_macros"); + else_macros = *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &kw["endif"]))[_val += _1] | +lit(';')); + else_macros.name("else_macros"); + + // Blocks do not require a separating semicolon. + block = + (kw["if"] > if_else_output(_r1)[_val = _1]) + // (kw["switch"] ... + ; + block.name("block"); + + // Statements require a separating semicolon. + statement = + (assignment_statement(_r1) [_val = _1]) + | (new_variable_statement(_r1)[_val = _1]) + | (conditional_expression(_r1)[px::bind(&expr::to_string2, _1, _val)]) ; - macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). // Also }{ could be replaced with ; to simplify writing of pure code. @@ -1897,10 +1912,6 @@ namespace client if_else_output.name("if_else_output"); if_text_block = (lit('}') > text_block(_r1) > '{'); if_text_block.name("if_text_block"); - if_macros = +lit(';') > (macro(_r1)[_val += _1] % (+lit(';')) | eps) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])); - if_macros.name("if_macros"); - else_macros = *lit(';') > (macro(_r1)[_val += _1] % (+lit(';')) | eps) > (+lit(';') | &kw["endif"]); - else_macros.name("else_macros"); // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). /* @@ -2127,7 +2138,7 @@ namespace client debug(start); debug(text); debug(text_block); - debug(macro); + debug(macros); debug(if_else_output); debug(interpolate_table); // debug(switch_output); @@ -2163,7 +2174,7 @@ namespace client // A free-form text, possibly empty, possibly containing macro expansions. qi::rule text_block; // Statements enclosed in curely braces {} - qi::rule macro, if_text_block, if_macros, else_macros; + qi::rule block, statement, macros, if_text_block, if_macros, else_macros; // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. qi::rule legacy_variable_expansion; // Parsed identifier name. diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 79cdfdea9..a6da48bf3 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -247,7 +247,7 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { } SECTION("nested if with new variables 2, mixing }{ with ;") { std::string script = - "{if 1 == 0;local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{if 1 == 0 then local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } @@ -259,22 +259,27 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { } SECTION("if with empty block and ;") { std::string script = - "{if false;else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{if false then else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } SECTION("nested if with new variables, two level, mixing }{ with ;") { std::string script = - "{if 1 == 1;if 2 == 3;nejaka / haluz;else;local myints = (6, 5, 4, 3, 2, 1);endif;else;if zase * haluz;else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif;endif}" + "{if 1 == 1 then if 2 == 3}nejaka / haluz{else local myints = (6, 5, 4, 3, 2, 1) endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } - SECTION("nested if with new variables, two level, ;, no semicolon after else") { + SECTION("nested if with new variables, two level, mixing }{ with ; 2") { std::string script = - "{if 1 == 1;if 2 == 3;nejaka / haluz;else local myints = (6, 5, 4, 3, 2, 1);endif;else if zase * haluz;else local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif;endif}" + "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } - SECTION("if else completely empty") { REQUIRE(parser.process("{if false; elsif false; else; endif}", 0, nullptr, nullptr, nullptr) == ""); } - SECTION("if else completely empty 2") { REQUIRE(parser.process("{if false; elsif false; else endif}", 0, nullptr, nullptr, nullptr) == ""); } + SECTION("nested if with new variables, two level, mixing }{ with ; 3") { + std::string script = + "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else}{if zase * haluz}{else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif}{endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("if else completely empty") { REQUIRE(parser.process("{if false then elsif false then else endif}", 0, nullptr, nullptr, nullptr) == ""); } } From 451097bcaca2be6c571d3985d6b1ed6e1d833f64 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 24 Mar 2023 13:53:12 +0100 Subject: [PATCH 172/201] Downscale axes when viewed from close-up --- src/slic3r/GUI/CoordAxes.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/CoordAxes.cpp b/src/slic3r/GUI/CoordAxes.cpp index 277c8ad30..fcb434f3b 100644 --- a/src/slic3r/GUI/CoordAxes.cpp +++ b/src/slic3r/GUI/CoordAxes.cpp @@ -44,17 +44,23 @@ void CoordAxes::render(const Transform3d& trafo, float emission_factor) shader->start_using(); shader->set_uniform("emission_factor", emission_factor); + // Scale the axes if the camera is close to them to avoid issues + // such as https://github.com/prusa3d/PrusaSlicer/issues/9483 + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d scale_tr = Transform3d::Identity(); + scale_tr.scale(std::min(1., camera.get_inv_zoom() * 10.)); + // x axis m_arrow.set_color(ColorRGBA::X()); - render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ 0.0, 0.5 * M_PI, 0.0 })); + render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ 0.0, 0.5 * M_PI, 0.0 }) * scale_tr); // y axis m_arrow.set_color(ColorRGBA::Y()); - render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ -0.5 * M_PI, 0.0, 0.0 })); + render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ -0.5 * M_PI, 0.0, 0.0 }) * scale_tr); // z axis m_arrow.set_color(ColorRGBA::Z()); - render_axis(*shader, trafo * Geometry::translation_transform(m_origin)); + render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * scale_tr); shader->stop_using(); if (curr_shader != nullptr) From fd457fb40bc3788cdf4a57f7aa5a21c923a5a94f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 27 Mar 2023 15:46:06 +0200 Subject: [PATCH 173/201] Revert "#9483 - Added keyboard shortcut CTRL+D to toggle the visibility of the printbed reference axes" This reverts commit f038eca52c34950ffa5227aebda27b3594f412b7. We will only use the axes that automatically scale, without the option to turn the rendering off. --- src/slic3r/GUI/GLCanvas3D.cpp | 12 +----------- src/slic3r/GUI/GLCanvas3D.hpp | 1 - src/slic3r/GUI/KBShortcutsDialog.cpp | 2 -- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dcc85dc65..08f68356f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1572,8 +1572,7 @@ void GLCanvas3D::render() _render_objects(GLVolumeCollection::ERenderType::Opaque); _render_sla_slices(); _render_selection(); - if (m_show_bed_axes) - _render_bed_axes(); + _render_bed_axes(); if (is_looking_downward) _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), false); if (!m_main_toolbar.is_enabled()) @@ -2351,15 +2350,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; -#ifdef __APPLE__ - case 'd': - case 'D': -#else /* __APPLE__ */ - case WXK_CONTROL_D: -#endif /* __APPLE__ */ - m_show_bed_axes = !m_show_bed_axes; - m_dirty = true; - break; #ifdef __APPLE__ case 'f': case 'F': diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6ebe7bcc6..a79bcff2e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -522,7 +522,6 @@ private: ECursorType m_cursor_type; GLSelectionRectangle m_rectangle_selection; std::vector m_hover_volume_idxs; - bool m_show_bed_axes{ true }; // Following variable is obsolete and it should be safe to remove it. // I just don't want to do it now before a release (Lukas Matena 24.3.2019) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index b2eb2c79d..66961b2cd 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -151,7 +151,6 @@ void KBShortcutsDialog::fill_shortcuts() { L("Arrow Right"), L("Move selection 10 mm in positive X direction") }, { std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") }, { ctrl + L("Any arrow"), L("Movement in camera space") }, - { ctrl + "D", L("Show/hide reference axes") }, { L("Page Up"), L("Rotate selection 45 degrees CCW") }, { L("Page Down"), L("Rotate selection 45 degrees CW") }, { "M", L("Gizmo move") }, @@ -232,7 +231,6 @@ void KBShortcutsDialog::fill_shortcuts() { "X", L("On/Off one layer mode of the vertical slider") }, { "L", L("Show/Hide legend") }, { "C", L("Show/Hide G-code window") }, - { ctrl + "D", L("Show/hide reference axes") }, }; m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts }); From 989ca9d8ee4ce442508d2ef949f561d551fc033c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Mar 2023 16:00:13 +0200 Subject: [PATCH 174/201] CutGizmo: Fix for Cut by Line --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index c65f4fad0..5d8a69e41 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -2298,7 +2298,10 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse const Vec3d new_plane_center = m_bb_center + cross_dir * cross_dir.dot(pt - m_bb_center); // update transformed bb const auto new_tbb = transformed_bounding_box(new_plane_center, m); - const Vec3d& instance_offset = m_parent.get_selection().get_first_volume()->get_instance_offset(); + const GLVolume* first_volume = m_parent.get_selection().get_first_volume(); + Vec3d instance_offset = first_volume->get_instance_offset(); + instance_offset[Z] += first_volume->get_sla_shift_z(); + const Vec3d trans_center_pos = m.inverse() * (new_plane_center - instance_offset) + new_tbb.center(); if (new_tbb.contains(trans_center_pos)) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); From 4764a701c14b7167c2ba0ce6b6747a419755f373 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 30 Jan 2023 17:06:27 +0100 Subject: [PATCH 175/201] Experiments with font size --- src/slic3r/GUI/GUI_App.cpp | 4 +- src/slic3r/GUI/GUI_Utils.hpp | 5 +- src/slic3r/GUI/MainFrame.cpp | 7 ++- src/slic3r/GUI/MainFrame.hpp | 2 +- src/slic3r/GUI/MsgDialog.cpp | 2 +- src/slic3r/GUI/Preferences.cpp | 74 ++++++++++++++++++++++++- src/slic3r/GUI/Preferences.hpp | 2 + src/slic3r/GUI/SavePresetDialog.cpp | 4 +- src/slic3r/GUI/Tab.cpp | 1 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 16 ++++-- 10 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1d4a53fd9..e03df09bb 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1322,7 +1322,7 @@ bool GUI_App::on_init_inner() if (!delayed_error_load_presets.empty()) show_error(nullptr, delayed_error_load_presets); - mainframe = new MainFrame(); + mainframe = new MainFrame(app_config->has("font_size") ? atoi(app_config->get("font_size").c_str()) : -1); // hide settings tabs after first Layout if (is_editor()) mainframe->select_tab(size_t(0)); @@ -1810,7 +1810,7 @@ void GUI_App::recreate_GUI(const wxString& msg_name) dlg.Update(10, _L("Recreating") + dots); MainFrame *old_main_frame = mainframe; - mainframe = new MainFrame(); + mainframe = new MainFrame(app_config->has("font_size") ? atoi(app_config->get("font_size").c_str()) : -1); if (is_editor()) // hide settings tabs after first Layout mainframe->select_tab(size_t(0)); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 55ca43248..624e7e1a3 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -82,7 +82,7 @@ template class DPIAware : public P { public: DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, - const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr) + const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name= wxFrameNameStr, const int font_point_size = -1) : P(parent, id, title, pos, size, style, name) { int dpi = get_dpi_for_window(this); @@ -90,6 +90,9 @@ public: m_prev_scale_factor = m_scale_factor; m_normal_font = get_default_font_for_dpi(this, dpi); + if (font_point_size > 0) + m_normal_font.SetPointSize(font_point_size); + /* Because of default window font is a primary display font, * We should set correct font for window before getting em_unit value. */ diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0c7805db8..4c9e8332a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -122,8 +122,8 @@ static wxIcon main_frame_icon(GUI_App::EAppMode app_mode) #endif // _WIN32 } -MainFrame::MainFrame() : -DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), +MainFrame::MainFrame(const int font_point_size) : +DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe", font_point_size), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) , m_recent_projects(9) , m_settings_dialog(this) @@ -1540,6 +1540,7 @@ void MainFrame::init_menubar_as_editor() // assign menubar to frame after appending items, otherwise special items // will not be handled correctly m_menubar = new wxMenuBar(); + m_menubar->SetFont(this->normal_font()); m_menubar->Append(fileMenu, _L("&File")); if (editMenu) m_menubar->Append(editMenu, _L("&Edit")); m_menubar->Append(windowMenu, _L("&Window")); @@ -2237,7 +2238,7 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const // ---------------------------------------------------------------------------- SettingsDialog::SettingsDialog(MainFrame* mainframe) -:DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog"), +:DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog", mainframe->normal_font().GetPointSize()), //: DPIDialog(mainframe, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, // wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"), m_main_frame(mainframe) diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 78ec13f64..c9fa601e1 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -139,7 +139,7 @@ protected: virtual void on_sys_color_changed() override; public: - MainFrame(); + MainFrame(const int font_point_size); ~MainFrame() = default; void update_layout(); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 43e13841c..cce159903 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -136,7 +136,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin msg_lines++; } - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + wxFont font = wxGetApp().normal_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont monospace = wxGetApp().code_font(); wxColour text_clr = wxGetApp().get_label_clr_default(); wxColour bgr_clr = parent->GetBackgroundColour(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 492e3fc37..69b6fb5fe 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -4,6 +4,7 @@ #include "Plater.hpp" #include "MsgDialog.hpp" #include "I18N.hpp" +#include "format.hpp" #include "libslic3r/AppConfig.hpp" #include #include "Notebook.hpp" @@ -549,6 +550,7 @@ void PreferencesDialog::build() create_settings_mode_widget(); create_settings_text_color_widget(); create_settings_mode_color_widget(); +// create_settings_font_widget(); m_optgroup_other = create_options_tab(_L("Other"), tabs); m_optgroup_other->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -580,6 +582,7 @@ void PreferencesDialog::build() activate_options_tab(m_optgroup_other); create_downloader_path_sizer(); + create_settings_font_widget(); #if ENABLE_ENVIRONMENT_MAP // Add "Render" tab @@ -694,7 +697,7 @@ void PreferencesDialog::accept(wxEvent&) bool update_filament_sidebar = (m_values.find("no_templates") != m_values.end()); - std::vector options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" }; + std::vector options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled", "font_size" }; for (const std::string& option : options_to_recreate_GUI) { if (m_values.find(option) != m_values.end()) { @@ -943,7 +946,6 @@ void PreferencesDialog::create_icon_size_slider() void PreferencesDialog::create_settings_mode_widget() { wxWindow* parent = m_optgroup_gui->parent(); - wxGetApp().UpdateDarkUI(parent); wxString title = L("Layout Options"); wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title)); @@ -1045,6 +1047,74 @@ void PreferencesDialog::create_settings_mode_color_widget() append_preferences_option_to_searcher(m_optgroup_gui, opt_key, title); } +void PreferencesDialog::create_settings_font_widget() +{ + wxWindow* parent = m_optgroup_other->parent(); + wxGetApp().UpdateDarkUI(parent); + + const wxString title = L("Application font size"); + wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title)); + if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT); + + const std::string opt_key = "font_size"; + m_blinkers[opt_key] = new BlinkingBitmap(parent); + + wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxHORIZONTAL); + + wxStaticText* font_example = new wxStaticText(parent, wxID_ANY, "Application text"); + int val = wxGetApp().normal_font().GetPointSize(); + wxSpinCtrl* size_sc = new wxSpinCtrl(parent, wxID_ANY, format_wxstr("%1%", val), wxDefaultPosition, wxSize(15*em_unit(), -1), wxTE_PROCESS_ENTER | wxSP_ARROW_KEYS +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + , 8, 20); + wxGetApp().UpdateDarkUI(size_sc); + + auto apply_font = [this, font_example, opt_key](const int val, const wxFont& font) { + font_example->SetFont(font); + m_values[opt_key] = format("%1%", val); + refresh_og(m_optgroup_other); + }; + + auto change_value = [size_sc, apply_font](wxCommandEvent& evt) { + const int val = size_sc->GetValue(); + wxFont font = wxGetApp().normal_font(); + font.SetPointSize(val); + + apply_font(val, font); + }; + size_sc->Bind(wxEVT_SPINCTRL, change_value); + size_sc->Bind(wxEVT_TEXT_ENTER, change_value); + + auto revert_btn = new ScalableButton(parent, wxID_ANY, "undo"); + revert_btn->SetToolTip(_L("Revert font to default")); + revert_btn->Bind(wxEVT_BUTTON, [size_sc, apply_font](wxEvent& event) { + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + const int val = font.GetPointSize(); + size_sc->SetValue(val); + apply_font(val, font); + }); + parent->Bind(wxEVT_UPDATE_UI, [size_sc](wxUpdateUIEvent& evt) { + const int def_size = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).GetPointSize(); + evt.Enable(def_size != size_sc->GetValue()); + }, revert_btn->GetId()); + + stb_sizer->Add(new wxStaticText(parent, wxID_ANY, _L("Font size") + ":"), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, em_unit()); + stb_sizer->Add(size_sc, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, em_unit()); + stb_sizer->Add(revert_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, em_unit()); + wxBoxSizer* font_sizer = new wxBoxSizer(wxVERTICAL); + font_sizer->Add(font_example, 1, wxALIGN_CENTER_HORIZONTAL); + stb_sizer->Add(font_sizer, 1, wxALIGN_CENTER_VERTICAL); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2); + sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL); + + m_optgroup_other->sizer->Add(sizer, 1, wxEXPAND | wxTOP, em_unit()); + + append_preferences_option_to_searcer(m_optgroup_other, opt_key, title); +} + void PreferencesDialog::create_downloader_path_sizer() { wxWindow* parent = m_optgroup_other->parent(); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 4ae93f6f9..765447f89 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -91,10 +91,12 @@ protected: void layout(); void clear_cache(); void refresh_og(std::shared_ptr og); + void refresh_og(ConfigOptionsGroup* og); void create_icon_size_slider(); void create_settings_mode_widget(); void create_settings_text_color_widget(); void create_settings_mode_color_widget(); + void create_settings_font_widget(); void create_downloader_path_sizer(); void init_highlighter(const t_config_option_key& opt_key); std::vector optgroups(); diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 09ae10207..a561a754e 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -311,11 +311,13 @@ SavePresetDialog::~SavePresetDialog() void SavePresetDialog::build(std::vector types, std::string suffix, bool template_filament) { + this->SetFont(wxGetApp().normal_font()); + #if defined(__WXMSW__) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT - this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); #else SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #endif // __WXMSW__ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3fb7bf605..d31f48913 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -265,6 +265,7 @@ void Tab::create_preset_tab() // tree m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); + m_treectrl->SetFont(wxGetApp().normal_font()); m_left_sizer->Add(m_treectrl, 1, wxEXPAND); // Index of the last icon inserted into m_treectrl m_icon_count = -1; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index ad702c1b3..5220b2115 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -837,7 +837,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT - this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + this->SetFont(wxGetApp().normal_font()); #endif // __WXMSW__ int border = 10; @@ -850,7 +851,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ add_new_value_column = false; m_action_line = new wxStaticText(this, wxID_ANY, ""); - m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); +// m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_action_line->SetFont(wxGetApp().bold_font()); m_tree = new DiffViewCtrl(this, wxSize(em * (add_new_value_column ? 80 : 60), em * 30)); m_tree->AppendToggleColumn_(L"\u2714" , DiffModel::colToggle, wxLinux ? 9 : 6); @@ -910,7 +912,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); }); m_info_line = new wxStaticText(this, wxID_ANY, ""); - m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); +// m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_info_line->SetFont(wxGetApp().bold_font()); m_info_line->Hide(); if (!m_app_config_key.empty()) { @@ -1362,6 +1365,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString : wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxGetApp().UpdateDarkUI(this); + this->SetFont(wxGetApp().normal_font()); int border = 10; bool has_new_value_column = !new_value_header.IsEmpty(); @@ -1528,7 +1532,8 @@ void DiffPresetDialog::create_show_all_presets_chb() void DiffPresetDialog::create_info_lines() { - const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); +// const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); + const wxFont font = GetFont().Bold(); m_top_info_line = new wxStaticText(this, wxID_ANY, _L("Select presets to compare")); m_top_info_line->SetFont(font); @@ -1668,7 +1673,8 @@ DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT - this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + this->SetFont(mainframe->normal_font()); #endif // __WXMSW__ // Init bundles From 47c318fa058c51c7c966579d7abec55a8e30ac0a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Mar 2023 16:54:54 +0200 Subject: [PATCH 176/201] Follow-up 4764a701c14b7167c2ba0ce6b6747a419755f373 - Fix a typo --- src/slic3r/GUI/Preferences.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 69b6fb5fe..bd49668c9 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -550,7 +550,6 @@ void PreferencesDialog::build() create_settings_mode_widget(); create_settings_text_color_widget(); create_settings_mode_color_widget(); -// create_settings_font_widget(); m_optgroup_other = create_options_tab(_L("Other"), tabs); m_optgroup_other->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -1112,7 +1111,7 @@ void PreferencesDialog::create_settings_font_widget() m_optgroup_other->sizer->Add(sizer, 1, wxEXPAND | wxTOP, em_unit()); - append_preferences_option_to_searcer(m_optgroup_other, opt_key, title); + append_preferences_option_to_searcher(m_optgroup_other, opt_key, title); } void PreferencesDialog::create_downloader_path_sizer() From c6ea4d19c9e0b77ab3fdeb8915014e524bad3ff0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Mar 2023 10:27:21 +0200 Subject: [PATCH 177/201] change identification of sla archives with svg images --- src/libslic3r/Format/SLAArchiveReader.cpp | 4 ++-- src/libslic3r/Format/SLAArchiveWriter.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Format/SLAArchiveReader.cpp b/src/libslic3r/Format/SLAArchiveReader.cpp index 7d549216c..b931ea0e4 100644 --- a/src/libslic3r/Format/SLAArchiveReader.cpp +++ b/src/libslic3r/Format/SLAArchiveReader.cpp @@ -37,8 +37,8 @@ static const std::map REGISTERED_ARCHIVES { [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique(fname, quality, progr); } } }, { - "SL2", - { L("SL2 archive files"), {"sl2", "sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives + "SL1SVG", + { L("SL1SVG archive files"), {"sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique(fname, quality, progr); }} }, // TODO: pwmx and future others. diff --git a/src/libslic3r/Format/SLAArchiveWriter.cpp b/src/libslic3r/Format/SLAArchiveWriter.cpp index babf92d0d..92f5fe3ec 100644 --- a/src/libslic3r/Format/SLAArchiveWriter.cpp +++ b/src/libslic3r/Format/SLAArchiveWriter.cpp @@ -26,8 +26,8 @@ static const std::map REGISTERED_ARCHIVES { { "sl1", [] (const auto &cfg) { return std::make_unique(cfg); } } }, { - "SL2", - { "sl2", [] (const auto &cfg) { return std::make_unique(cfg); } } + "SL1SVG", + { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } }, { "pwmx", From 4b6fe6108310077a4676a83d40aa9b423b0967de Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Mar 2023 10:58:07 +0200 Subject: [PATCH 178/201] Prevent crash if archive format is invalid Throw exception instead and display it's error message --- src/libslic3r/SLAPrint.cpp | 9 +++++++++ src/libslic3r/SLAPrint.hpp | 5 +---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 6dc4a4620..8238a266e 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -588,6 +588,15 @@ std::string SLAPrint::validate(std::string*) const return ""; } +void SLAPrint::export_print(const std::string &fname, const ThumbnailsList &thumbnails, const std::string &projectname) +{ + if (m_archiver) + m_archiver->export_print(fname, *this, thumbnails, projectname); + else { + throw ExportError(format(_u8L("Unknown archive format: %s"), m_printer_config.sla_archive_format.value)); + } +} + bool SLAPrint::invalidate_step(SLAPrintStep step) { bool invalidated = Inherited::invalidate_step(step); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index bd8424ca7..126941d82 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -546,10 +546,7 @@ public: void export_print(const std::string &fname, const ThumbnailsList &thumbnails, - const std::string &projectname = "") - { - m_archiver->export_print(fname, *this, thumbnails, projectname); - } + const std::string &projectname = ""); private: From f1dd85309b11b63d579900a80f45ac11a647af7e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Mar 2023 12:11:04 +0200 Subject: [PATCH 179/201] Add back sla svg export alias --- src/libslic3r/Format/SLAArchiveWriter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libslic3r/Format/SLAArchiveWriter.cpp b/src/libslic3r/Format/SLAArchiveWriter.cpp index 92f5fe3ec..b28c2c680 100644 --- a/src/libslic3r/Format/SLAArchiveWriter.cpp +++ b/src/libslic3r/Format/SLAArchiveWriter.cpp @@ -29,6 +29,10 @@ static const std::map REGISTERED_ARCHIVES { "SL1SVG", { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } }, + { + "SL2", + { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } + }, { "pwmx", { "pwmx", [] (const auto &cfg) { return std::make_unique(cfg); } } From 59552a8aee893316a74b9a6e012d164ff7643b44 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 28 Mar 2023 12:47:23 +0200 Subject: [PATCH 180/201] PlaceholderParser & G-code export: Exchange of position & extrusion status between custom G-codes and slicer, extension of script syntax. Newly each custom G-code block may exchange the following values with slicer: Position and Z-hop: position (read/write)- 3 element vector (X, Y, Z) of current G-code position. Z element contains the current Z hop. zhop (read only)- initial zhop value Extruders: vector variables, one element per extruder: e_position (read/write) - absolute E position, only available with absolute extruder addressing e_retracted (read/write) - current retraction state e_restart_extra (read/write) - current planned extra deretraction when starting printing For readibility, script's if / elsif / else / endif syntax was modified: {if cond then block elsif cond then block else block endif} Semicolon is not required after else or endif. --- src/libslic3r/Extruder.cpp | 19 ++++ src/libslic3r/Extruder.hpp | 13 +++ src/libslic3r/GCode.cpp | 188 +++++++++++++++++++++++++++------- src/libslic3r/GCode.hpp | 48 ++++++--- src/libslic3r/GCodeWriter.cpp | 14 +++ src/libslic3r/GCodeWriter.hpp | 11 ++ 6 files changed, 243 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index d2ff65097..b1a089d08 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -37,6 +37,7 @@ std::pair Extruder::extrude(double dE) value supplied will overwrite the previous one if any. */ std::pair Extruder::retract(double retract_length, double restart_extra) { + assert(restart_extra >= 0); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -64,6 +65,24 @@ std::pair Extruder::unretract() return std::make_pair(dE, emitE); } +// Setting the retract state from the script. +// Sets current retraction value & restart extra filament amount if retracted > 0. +void Extruder::set_retracted(double retracted, double restart_extra) +{ + if (retracted < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative z_retracted."); + if (restart_extra < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative z_restart_extra."); + + if (retracted > EPSILON) { + m_retracted = retracted; + m_restart_extra = restart_extra < EPSILON ? 0 : restart_extra; + } else { + m_retracted = 0; + m_restart_extra = 0; + } +} + // Used filament volume in mm^3. double Extruder::extruded_volume() const { diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp index 7491b1c8f..7c37f1934 100644 --- a/src/libslic3r/Extruder.hpp +++ b/src/libslic3r/Extruder.hpp @@ -36,6 +36,19 @@ public: double extruded_volume() const; // Used filament length in mm. double used_filament() const; + + // Getters for the PlaceholderParser. + // Get current extruder position. Only applicable with absolute extruder addressing. + double position() const { return m_E; } + // Get current retraction value. Only non-negative values. + double retracted() const { return m_retracted; } + // Get extra retraction planned after + double restart_extra() const { return m_restart_extra; } + // Setters for the PlaceholderParser. + // Set current extruder position. Only applicable with absolute extruder addressing. + void set_position(double e) { m_E = e; } + // Sets current retraction value & restart extra filament amount if retracted > 0. + void set_retracted(double retracted, double restart_extra); double filament_diameter() const; double filament_crossection() const { return this->filament_diameter() * this->filament_diameter() * 0.25 * PI; } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d4278ed74..22c26b3a5 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -419,7 +419,7 @@ namespace Slic3r { std::string WipeTowerIntegration::finalize(GCode& gcodegen) { std::string gcode; - if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) + if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) gcode += gcodegen.change_layer(m_final_purge.print_z); gcode += append_tcr(gcodegen, m_final_purge, -1); return gcode; @@ -429,6 +429,90 @@ namespace Slic3r { #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) +void GCode::PlaceholderParserIntegration::reset() +{ + this->failed_templates.clear(); + this->output_config.clear(); + this->opt_position = nullptr; + this->opt_zhop = nullptr; + this->opt_e_position = nullptr; + this->opt_e_retracted = nullptr; + this->opt_e_restart_extra = nullptr; + this->num_extruders = 0; + this->position.clear(); + this->e_position.clear(); + this->e_retracted.clear(); + this->e_restart_extra.clear(); +} + +void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) +{ + this->reset(); + const std::vector &extruders = writer.extruders(); + if (! extruders.empty()) { + this->num_extruders = extruders.back().id() + 1; + this->e_retracted.assign(num_extruders, 0); + this->e_restart_extra.assign(num_extruders, 0); + this->opt_e_retracted = new ConfigOptionFloats(e_retracted); + this->opt_e_restart_extra = new ConfigOptionFloats(e_restart_extra); + this->output_config.set_key_value("e_retracted", this->opt_e_retracted); + this->output_config.set_key_value("e_restart_extra", this->opt_e_restart_extra); + if (! writer.config.use_relative_e_distances) { + e_position.assign(num_extruders, 0); + opt_e_position = new ConfigOptionFloats(e_position); + this->output_config.set_key_value("e_position", opt_e_position); + } + } + + // Reserve buffer for current position. + this->position.assign(3, 0); + this->opt_position = new ConfigOptionFloats(this->position); + // Store zhop variable into the parser itself, it is a read-only variable to the script. + this->opt_zhop = new ConfigOptionFloat(writer.get_zhop()); + this->parser.set("zhop", this->opt_zhop); +} + +void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) +{ + memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3); + this->opt_position->values = this->position; + this->opt_zhop->value = writer.get_zhop(); + + if (this->num_extruders > 0) { + const std::vector &extruders = writer.extruders(); + assert(! extruders.empty() && num_extruders == extruders.back().id() + 1); + this->e_retracted.assign(num_extruders, 0); + this->e_restart_extra.assign(num_extruders, 0); + for (const Extruder &e : extruders) { + this->e_retracted[e.id()] = e.retracted(); + this->e_restart_extra[e.id()] = e.restart_extra(); + } + opt_e_retracted->values = this->e_retracted; + opt_e_restart_extra->values = this->e_restart_extra; + if (! writer.config.use_relative_e_distances) { + this->e_position.assign(num_extruders, 0); + for (const Extruder &e : extruders) + this->e_position[e.id()] = e.position(); + this->opt_e_position->values = this->e_position; + } + } +} + +// Throw if any of the output vector variables were resized by the script. +void GCode::PlaceholderParserIntegration::validate_output_vector_variables() +{ + if (this->opt_position->values.size() != 3) + throw Slic3r::RuntimeError("\"position\" output variable must not be resized by the script."); + if (this->num_extruders > 0) { + if (this->opt_e_position && this->opt_e_position->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_position\" output variable must not be resized by the script."); + if (this->opt_e_retracted->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_retracted\" output variable must not be resized by the script."); + if (this->opt_e_restart_extra->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_restart_extra\" output variable must not be resized by the script."); + } +} + // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& object) @@ -728,7 +812,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); try { - m_placeholder_parser_failed_templates.clear(); this->_do_export(*print, file, thumbnail_cb); file.flush(); if (file.is_error()) { @@ -745,11 +828,11 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu } file.close(); - if (! m_placeholder_parser_failed_templates.empty()) { + if (! m_placeholder_parser_integration.failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. //FIXME localize! std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; - for (const auto &name_and_error : m_placeholder_parser_failed_templates) + for (const auto &name_and_error : m_placeholder_parser_integration.failed_templates) msg += name_and_error.first + "\n" + name_and_error.second + "\n"; msg += "\nPlease inspect the file "; msg += path_tmp + " for error messages enclosed between\n"; @@ -1078,12 +1161,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.find_replace_enable(); // Prepare the helper object for replacing placeholders in custom G-code and output filename. - m_placeholder_parser = print.placeholder_parser(); - m_placeholder_parser.update_timestamp(); - m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + m_placeholder_parser_integration.parser = print.placeholder_parser(); + m_placeholder_parser_integration.parser.update_timestamp(); + m_placeholder_parser_integration.context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); // Enable passing global variables between PlaceholderParser invocations. - m_placeholder_parser_context.global_config = std::make_unique(); - print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode"); + m_placeholder_parser_integration.context.global_config = std::make_unique(); + print.update_object_placeholders(m_placeholder_parser_integration.parser.config_writable(), ".gcode"); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. // For a print by objects, find the 1st printing object. @@ -1147,23 +1230,25 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Emit machine envelope limits for the Marlin firmware. this->print_machine_envelope(file, print); + // Update output variables after the extruders were initialized. + m_placeholder_parser_integration.init(m_writer); // Let the start-up script prime the 1st printing tool. - m_placeholder_parser.set("initial_tool", initial_extruder_id); - m_placeholder_parser.set("initial_extruder", initial_extruder_id); - m_placeholder_parser.set("current_extruder", initial_extruder_id); + this->placeholder_parser().set("initial_tool", initial_extruder_id); + this->placeholder_parser().set("initial_extruder", initial_extruder_id); + this->placeholder_parser().set("current_extruder", initial_extruder_id); //Set variable for total layer count so it can be used in custom gcode. - m_placeholder_parser.set("total_layer_count", m_layer_count); + this->placeholder_parser().set("total_layer_count", m_layer_count); // Useful for sequential prints. - m_placeholder_parser.set("current_object_idx", 0); + this->placeholder_parser().set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. - m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); - m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); - m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + this->placeholder_parser().set("has_wipe_tower", has_wipe_tower); + this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); + this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). { BoundingBoxf bbox(print.config().bed_shape.values); - m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); - m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); } { // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. @@ -1176,15 +1261,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato for (const Point &pt : print.first_layer_convex_hull().points) pts->values.emplace_back(unscale(pt)); BoundingBoxf bbox(pts->values); - m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); - m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); - m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + this->placeholder_parser().set("first_layer_print_convex_hull", pts.release()); + this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); std::vector is_extruder_used(print.config().nozzle_diameter.size(), 0); for (unsigned int extruder_id : tool_ordering.all_extruders()) is_extruder_used[extruder_id] = true; - m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); + this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); } // Enable ooze prevention if configured so. @@ -1251,7 +1336,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. - m_placeholder_parser.set("current_object_idx", int(finished_objects)); + this->placeholder_parser().set("current_object_idx", int(finished_objects)); std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); @@ -1337,7 +1422,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); if (print.config().single_extruder_multi_material) { // Process the end_filament_gcode for the active filament only. @@ -1560,17 +1645,46 @@ void GCode::process_layers( output_stream.find_replace_enable(); } -std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) +std::string GCode::placeholder_parser_process( + const std::string &name, + const std::string &templ, + unsigned int current_extruder_id, + const DynamicConfig *config_override) { + PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { - return m_placeholder_parser.process(templ, current_extruder_id, config_override, &m_placeholder_parser_context); - } catch (std::runtime_error &err) { + ppi.update_from_gcodewriter(m_writer); + std::string output = ppi.parser.process(templ, current_extruder_id, config_override, &ppi.output_config, &ppi.context); + ppi.validate_output_vector_variables(); + + if (const std::vector &pos = ppi.opt_position->values; ppi.position != pos) { + // Update G-code writer. + m_writer.update_position({ pos[0], pos[1], pos[2] }); + this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] })); + } + + for (const Extruder &e : m_writer.extruders()) { + unsigned int eid = e.id(); + assert(eid < ppi.num_extruders); + if ( eid < ppi.num_extruders) { + if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid])) + const_cast(e).set_position(ppi.opt_e_position->values[eid]); + if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) || + ! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid])) + const_cast(e).set_retracted(ppi.opt_e_retracted->values[eid], ppi.opt_e_restart_extra->values[eid]); + } + } + + return output; + } + catch (std::runtime_error &err) + { // Collect the names of failed template substitutions for error reporting. - auto it = m_placeholder_parser_failed_templates.find(name); - if (it == m_placeholder_parser_failed_templates.end()) + auto it = ppi.failed_templates.find(name); + if (it == ppi.failed_templates.end()) // Only if there was no error reported for this template, store the first error message into the map to be reported. // We don't want to collect error message for each and every occurence of a single custom G-code section. - m_placeholder_parser_failed_templates.insert(it, std::make_pair(name, std::string(err.what()))); + ppi.failed_templates.insert(it, std::make_pair(name, std::string(err.what()))); // Insert the macro error message into the G-code. return std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" + @@ -3160,7 +3274,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // if we are running a single-extruder setup, just set the extruder and return nothing if (!m_writer.multiple_extruders) { - m_placeholder_parser.set("current_extruder", extruder_id); + this->placeholder_parser().set("current_extruder", extruder_id); std::string gcode; // Append the filament start G-code. @@ -3169,7 +3283,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the start_filament_gcode for the filament. DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); @@ -3233,7 +3347,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) gcode += m_writer.set_temperature(temp, false); } - m_placeholder_parser.set("current_extruder", extruder_id); + this->placeholder_parser().set("current_extruder", extruder_id); // Append the filament start G-code. const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); @@ -3241,7 +3355,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the start_filament_gcode for the new filament. DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index ee50aefcc..98f49095d 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -173,8 +173,8 @@ public: const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } const GCodeWriter& writer() const { return m_writer; } - PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } - const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser_integration.parser; } + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser_integration.parser; } // Process a template through the placeholder parser, collect error messages to be reported // inside the generated string and after the G-code export finishes. std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); @@ -343,11 +343,33 @@ private: // scaled G-code resolution double m_scaled_resolution; GCodeWriter m_writer; - PlaceholderParser m_placeholder_parser; - // For random number generator etc. - PlaceholderParser::ContextData m_placeholder_parser_context; - // Collection of templates, on which the placeholder substitution failed. - std::map m_placeholder_parser_failed_templates; + + struct PlaceholderParserIntegration { + void reset(); + void init(const GCodeWriter &config); + void update_from_gcodewriter(const GCodeWriter &writer); + void validate_output_vector_variables(); + + PlaceholderParser parser; + // For random number generator etc. + PlaceholderParser::ContextData context; + // Collection of templates, on which the placeholder substitution failed. + std::map failed_templates; + // Input/output from/to custom G-code block, for returning position, retraction etc. + DynamicConfig output_config; + ConfigOptionFloats *opt_position { nullptr }; + ConfigOptionFloat *opt_zhop { nullptr }; + ConfigOptionFloats *opt_e_position { nullptr }; + ConfigOptionFloats *opt_e_retracted { nullptr }; + ConfigOptionFloats *opt_e_restart_extra { nullptr }; + // Caches of the data passed to the script. + size_t num_extruders; + std::vector position; + std::vector e_position; + std::vector e_retracted; + std::vector e_restart_extra; + } m_placeholder_parser_integration; + OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; @@ -403,15 +425,15 @@ private: // Index of a last object copy extruded. std::pair m_last_obj_copy; - bool m_silent_time_estimator_enabled; + bool m_silent_time_estimator_enabled; // Processor - GCodeProcessor m_processor; + GCodeProcessor m_processor; - std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1); - void print_machine_envelope(GCodeOutputStream &file, Print &print); - void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); - void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1); + void print_machine_envelope(GCodeOutputStream &file, Print &print); + void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); // On the first printing layer. This flag triggers first layer speeds. bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; } // To control print speed of 1st object layer over raft interface. diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 5080fabb4..9c330c38e 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -486,6 +486,20 @@ std::string GCodeWriter::unlift() return gcode; } +void GCodeWriter::update_position(const Vec3d &new_pos) +{ + assert(this->m_lifted >= 0); + const double nominal_z = m_pos.z() - m_lifted; + m_lifted = new_pos.z() - nominal_z; + if (m_lifted < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative Z-hop. Final Z position is below the print_z height."); + // In case that retract_lift == layer_height we could end up with almost zero in_m_lifted + // and a retract could be skipped (https://github.com/prusa3d/PrusaSlicer/issues/2154 + if (m_lifted < EPSILON) + m_lifted = 0.; + m_pos = new_pos; +} + std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed) { std::ostringstream gcode; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 9e5fce702..0d376cb15 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -68,7 +68,18 @@ public: std::string unretract(); std::string lift(); std::string unlift(); + + // Current position of the printer, in G-code coordinates. + // Z coordinate of current position contains zhop. If zhop is applied (this->zhop() > 0), + // then the print_z = this->get_position().z() - this->zhop(). Vec3d get_position() const { return m_pos; } + // Current Z hop value. + double get_zhop() const { return m_lifted; } + // Update position of the print head based on the final position returned by a custom G-code block. + // The new position Z coordinate contains the Z-hop. + // GCodeWriter expects the custom script to NOT change print_z, only Z-hop, thus the print_z is maintained + // by this function while the current Z-hop accumulator is updated. + void update_position(const Vec3d &new_pos); // Returns whether this flavor supports separate print and travel acceleration. static bool supports_separate_travel_acceleration(GCodeFlavor flavor); From 95d3a2b2ec516d28b9e5c62555eb7d15be5e34cf Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Mar 2023 12:57:34 +0200 Subject: [PATCH 181/201] Fix CGAL exception preventing preview generation fallback SPE-1617 --- src/libslic3r/SLAPrintSteps.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 5ef8a2709..b73a8c4fb 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -199,7 +199,13 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st m = csgmesh_merge_positive_parts(r); handled = true; } else if (csg::check_csgmesh_booleans(r) == r.end()) { - auto cgalmeshptr = csg::perform_csgmesh_booleans(r); + MeshBoolean::cgal::CGALMeshPtr cgalmeshptr; + try { + cgalmeshptr = csg::perform_csgmesh_booleans(r); + } catch (...) { + // leaves cgalmeshptr as nullptr + } + if (cgalmeshptr) { m = MeshBoolean::cgal::cgal_to_indexed_triangle_set(*cgalmeshptr); handled = true; From e65ebccb7f5b3f86c50361bd3bd6cc3ebc97d437 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 28 Mar 2023 13:39:53 +0200 Subject: [PATCH 182/201] OSX specific: EmbossGizmo: Fixed for SPE-1616 - Crash on MacOS when text in emboss gizmo is moved --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 8c4b4ff70..e7220481c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -369,6 +369,10 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager, up_limit); bool is_dragging = m_surface_drag.has_value(); + // Check if selection is still active + if (m_volume == nullptr) + return false; + // End with surface dragging? if (was_dragging && !is_dragging) { // Update surface by new position From 41bf6aeab8ba26e57addf24e6d450789f9cd14f8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 28 Mar 2023 13:54:11 +0200 Subject: [PATCH 183/201] EmbossGizmo: Fixed a text of the tooltip for "Keep rotation" option --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e7220481c..ae3704b52 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -773,7 +773,7 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() tr.skew_ration = _u8L("Skew ratio"); tr.from_surface = _u8L("From surface"); tr.rotation = _u8L("Rotation"); - tr.keep_up = _u8L("Keep Up"); + tr.keep_up = "Keep Rotation"; tr.collection = _u8L("Collection"); float max_advanced_text_width = std::max({ @@ -2995,7 +2995,7 @@ void GLGizmoEmboss::draw_advanced() } } if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Keep text orientation during surface dragging.\nNot stable between horizontal and vertical alignment.").c_str()); + ImGui::SetTooltip("%s", _u8L("Lock the text's rotation when moving text along the object's surface.").c_str()); // when more collection add selector if (ff.font_file->infos.size() > 1) { From d798bc62aeaab6ba61199abc2fa32aa32bf175a6 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 28 Mar 2023 14:07:28 +0200 Subject: [PATCH 184/201] Sl1host inherits from prusalink --- src/slic3r/Utils/OctoPrint.cpp | 23 ++------------- src/slic3r/Utils/OctoPrint.hpp | 52 +++++++++++++++------------------- 2 files changed, 25 insertions(+), 50 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 540852af8..3824c8ed4 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -512,11 +512,8 @@ std::string OctoPrint::make_url(const std::string &path) const } } -SL1Host::SL1Host(DynamicPrintConfig *config) : - OctoPrint(config), - m_authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), - m_username(config->opt_string("printhost_user")), - m_password(config->opt_string("printhost_password")) +SL1Host::SL1Host(DynamicPrintConfig *config) + : PrusaLink(config) { } @@ -538,22 +535,6 @@ bool SL1Host::validate_version_text(const boost::optional &version_ return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; } -void SL1Host::set_auth(Http &http) const -{ - switch (m_authorization_type) { - case atKeyPassword: - http.header("X-Api-Key", get_apikey()); - break; - case atUserPassword: - http.auth_digest(m_username, m_password); - break; - } - - if (! get_cafile().empty()) { - http.ca_file(get_cafile()); - } -} - // PrusaLink PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) : OctoPrint(config), diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 82c07f6f4..2daeab73f 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -55,30 +55,6 @@ private: #endif }; -class SL1Host: public OctoPrint -{ -public: - SL1Host(DynamicPrintConfig *config); - ~SL1Host() override = default; - - const char* get_name() const override; - - wxString get_test_ok_msg() const override; - wxString get_test_failed_msg(wxString &msg) const override; - PrintHostPostUploadActions get_post_upload_actions() const override { return {}; } - -protected: - bool validate_version_text(const boost::optional &version_text) const override; - -private: - void set_auth(Http &http) const override; - - // Host authorization type. - AuthorizationType m_authorization_type; - // username and password for HTTP Digest Authentization (RFC RFC2617) - std::string m_username; - std::string m_password; -}; class PrusaLink : public OctoPrint { @@ -106,6 +82,12 @@ protected: bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override; #endif + // Host authorization type. + AuthorizationType m_authorization_type; + // username and password for HTTP Digest Authentization (RFC RFC2617) + std::string m_username; + std::string m_password; + private: bool test_with_method_check(wxString& curl_msg, bool& use_put) const; bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const; @@ -113,11 +95,7 @@ private: #ifdef WIN32 bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const; #endif - // Host authorization type. - AuthorizationType m_authorization_type; - // username and password for HTTP Digest Authentization (RFC RFC2617) - std::string m_username; - std::string m_password; + bool m_show_after_message; #if 0 @@ -149,6 +127,22 @@ public: const char* get_name() const override { return "Mainsail/Fluidd"; } }; +class SL1Host : public PrusaLink +{ +public: + SL1Host(DynamicPrintConfig* config); + ~SL1Host() override = default; + + const char* get_name() const override; + + wxString get_test_ok_msg() const override; + wxString get_test_failed_msg(wxString& msg) const override; + PrintHostPostUploadActions get_post_upload_actions() const override { return {}; } + +protected: + bool validate_version_text(const boost::optional& version_text) const override; +}; + } #endif From 4e64e7e74fa90eed0c40bd3432f2696b804dfb19 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Feb 2023 11:06:19 +0100 Subject: [PATCH 185/201] Export boolean-ed models if possible to STL/obj files --- src/slic3r/GUI/Plater.cpp | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3595276af..6fb7521c4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -54,6 +54,10 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/miniz_extension.hpp" +// For stl export +#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp" +#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp" + #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -6382,14 +6386,34 @@ void Plater::export_stl_obj(bool extended, bool selection_only) return; // Following lambda generates a combined mesh for export with normals pointing outwards. - auto mesh_to_export_fff = [](const ModelObject& mo, int instance_id) { + auto mesh_to_export_fff = [this](const ModelObject& mo, int instance_id) { TriangleMesh mesh; - for (const ModelVolume* v : mo.volumes) - if (v->is_model_part()) { - TriangleMesh vol_mesh(v->mesh()); - vol_mesh.transform(v->get_matrix(), true); - mesh.merge(vol_mesh); - } + + std::vector csgmesh; + csgmesh.reserve(2 * mo.volumes.size()); + csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh), + csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits); + + if (csg::check_csgmesh_booleans(range(csgmesh)) == csgmesh.end()) { + try { + auto cgalm = csg::perform_csgmesh_booleans(range(csgmesh)); + mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalm); + } catch (...) {} + } + + if (mesh.empty()) { + get_notification_manager()->push_plater_warning_notification( + _u8L("Unable to perform boolean operation on model meshes. " + "Only positive parts will be exported.")); + + for (const ModelVolume* v : mo.volumes) + if (v->is_model_part()) { + TriangleMesh vol_mesh(v->mesh()); + vol_mesh.transform(v->get_matrix(), true); + mesh.merge(vol_mesh); + } + } + if (instance_id == -1) { TriangleMesh vols_mesh(mesh); mesh = TriangleMesh(); From 5a586b2b4ec43de5651a30f623967e2427690c45 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 14 Mar 2023 12:55:45 +0100 Subject: [PATCH 186/201] Fix failed export message when cgal errors are detected on target meshes --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6fb7521c4..159a9693d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6402,7 +6402,7 @@ void Plater::export_stl_obj(bool extended, bool selection_only) } if (mesh.empty()) { - get_notification_manager()->push_plater_warning_notification( + get_notification_manager()->push_plater_error_notification( _u8L("Unable to perform boolean operation on model meshes. " "Only positive parts will be exported.")); From 56d15253d41953e2a319f7cf195f1059e6a0075a Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Mar 2023 16:07:00 +0200 Subject: [PATCH 187/201] Fix update of model after finish text jobs. --- src/slic3r/GUI/Jobs/EmbossJob.cpp | 65 +++++++++++++++++++++++-------- src/slic3r/GUI/Jobs/EmbossJob.hpp | 2 +- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 208cb2c0b..a265c4de9 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -483,10 +483,46 @@ TriangleMesh priv::create_default_mesh() return triangle_mesh; } +namespace{ +void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list) +{ + if (obj_list == nullptr) + return; + + const std::vector* objects = obj_list->objects(); + if (objects == nullptr) + return; + + int object_idx = -1; + int volume_idx = -1; + for (size_t oi = 0; oi < objects->size(); ++oi) { + const ModelObject *mo = objects->at(oi); + if (mo == nullptr) + continue; + if (volume.get_object()->id() != mo->id()) + continue; + const ModelVolumePtrs& volumes = mo->volumes; + for (size_t vi = 0; vi < volumes.size(); ++vi) { + const ModelVolume *mv = volumes[vi]; + if (mv == nullptr) + continue; + if (mv->id() == volume.id()){ + object_idx = static_cast(oi); + volume_idx = static_cast(vi); + break; + } + } + if (volume_idx > 0) + break; + } + obj_list->update_name_in_list(object_idx, volume_idx); +} +} + void UpdateJob::update_volume(ModelVolume *volume, TriangleMesh &&mesh, const TextConfiguration &text_configuration, - const std::string &volume_name) + std::string_view volume_name) { // check inputs bool is_valid_input = @@ -506,19 +542,12 @@ void UpdateJob::update_volume(ModelVolume *volume, // discard information about rotation, should not be stored in volume volume->text_configuration->style.prop.angle.reset(); - GUI_App &app = wxGetApp(); // may be move to input - GLCanvas3D *canvas = app.plater()->canvas3D(); - const Selection &selection = canvas->get_selection(); - const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); - int object_idx = gl_volume->object_idx(); + GUI_App &app = wxGetApp(); // may be move ObjectList and Plater to input? + // update volume name in right panel( volume / object name) if (volume->name != volume_name) { volume->name = volume_name; - - // update volume name in right panel( volume / object name) - int volume_idx = gl_volume->volume_idx(); - ObjectList *obj_list = app.obj_list(); - obj_list->update_name_in_list(object_idx, volume_idx); + update_volume_name(*volume, app.obj_list()); } // When text is object. @@ -528,11 +557,12 @@ void UpdateJob::update_volume(ModelVolume *volume, volume->get_object()->ensure_on_bed(); // redraw scene - bool refresh_immediately = false; - canvas->reload_scene(refresh_immediately); + Plater *plater = app.plater(); + if (plater == nullptr) + return; - // Change buttons "Export G-code" into "Slice now" - canvas->post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + // Update Model and redraw scene + plater->update(); } void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3d* tr) @@ -646,8 +676,9 @@ void priv::create_volume( if (manager.get_current_type() != GLGizmosManager::Emboss) manager.open_gizmo(GLGizmosManager::Emboss); - // redraw scene - canvas->reload_scene(true); + // update model and redraw scene + //canvas->reload_scene(true); + plater->update(); } ModelVolume *priv::get_volume(ModelObjectPtrs &objects, diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index d3896c0a6..d4b32cf61 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -145,7 +145,7 @@ public: static void update_volume(ModelVolume *volume, TriangleMesh &&mesh, const TextConfiguration &text_configuration, - const std::string &volume_name); + std::string_view volume_name); }; struct SurfaceVolumeData From 91f6530acff15893eeb3e1cd56ac9372e56712fa Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 28 Mar 2023 16:41:08 +0200 Subject: [PATCH 188/201] ObjectList: Fixed selection, when object part is moving/rotating/scaling and "Sinking" info is appearing/disappearing. + Reverted "patch" commit e65ebccb7f5b3f86c50361bd3bd6cc3ebc97d437 --- src/slic3r/GUI/GUI_ObjectList.cpp | 10 ++++++++++ src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 4 ---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index ad2758f9e..5f8b9a75b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2939,6 +2939,16 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio if (obj_idx >= m_objects->size()) return; + wxDataViewItemArray sels; + if (!selections) { + GetSelections(sels); + for (wxDataViewItem item : sels) + if (item.IsOk() && m_objects_model->GetItemType(item) == itVolume) { + selections = &sels; + break; + } + } + const ModelObject* model_object = (*m_objects)[obj_idx]; wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); assert(item_obj.IsOk()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index ae3704b52..ec2a74f96 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -369,10 +369,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager, up_limit); bool is_dragging = m_surface_drag.has_value(); - // Check if selection is still active - if (m_volume == nullptr) - return false; - // End with surface dragging? if (was_dragging && !is_dragging) { // Update surface by new position From 0cb1c74ec2753b2360798399ee4e9455e8d80ddd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 28 Mar 2023 16:47:14 +0200 Subject: [PATCH 189/201] Disabled font experiments --- src/slic3r/GUI/Preferences.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index bd49668c9..bf1169d82 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -581,7 +581,7 @@ void PreferencesDialog::build() activate_options_tab(m_optgroup_other); create_downloader_path_sizer(); - create_settings_font_widget(); +// create_settings_font_widget(); #if ENABLE_ENVIRONMENT_MAP // Add "Render" tab From 392dbba96f13026041e97f6d0e8042d05e326423 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Mar 2023 17:10:56 +0200 Subject: [PATCH 190/201] Fix offsets for SLA Text dragging with visible supports. --- src/slic3r/GUI/SurfaceDrag.cpp | 72 ++++++++++++---------------------- src/slic3r/GUI/SurfaceDrag.hpp | 3 ++ 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index a202ae5de..40b01a2af 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -9,50 +9,6 @@ #include "libslic3r/Emboss.hpp" namespace Slic3r::GUI { - -/// -/// Calculate offset from mouse position to center of text -/// -/// Position on screen[in Px] e.g. mouse position -/// Selected volume(text) -/// Actual position and view direction of camera -/// Offset in screen coordinate -static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera) -{ - const Transform3d &volume_tr = volume.get_matrix(); - assert(volume.text_configuration.has_value()); - - auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d { - Transform3d to_world = instrance_tr * volume_tr; - - // Use fix of .3mf loaded tranformation when exist - if (volume.text_configuration->fix_3mf_tr.has_value()) - to_world = to_world * (*volume.text_configuration->fix_3mf_tr); - // zero point of volume in world coordinate system - Vec3d volume_center = to_world.translation(); - // screen coordinate of volume center - Vec2i coor = CameraUtils::project(camera, volume_center); - return coor.cast() - screen_coor; - }; - - auto object = volume.get_object(); - assert(!object->instances.empty()); - // Speed up for one instance - if (object->instances.size() == 1) - return calc_offset(object->instances.front()->get_matrix()); - - Vec2d nearest_offset; - double nearest_offset_size = std::numeric_limits::max(); - for (const ModelInstance *instance : object->instances) { - Vec2d offset = calc_offset(instance->get_matrix()); - double offset_size = offset.norm(); - if (nearest_offset_size < offset_size) - continue; - nearest_offset_size = offset_size; - nearest_offset = offset; - } - return nearest_offset; -} // Calculate scale in world for check in debug [[maybe_unused]] static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) @@ -109,7 +65,8 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, gl_volumes[hovered_idx_] != gl_volume) return false; - const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + const ModelObjectPtrs &objects = canvas.get_model()->objects; + const ModelObject *object = get_model_object(*gl_volume, objects); assert(object != nullptr); if (object == nullptr) return false; @@ -148,7 +105,26 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); - Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera); + + // world_matrix_fixed() without sla shift + Transform3d to_world = world_matrix_fixed(*gl_volume, objects); + + // zero point of volume in world coordinate system + Vec3d volume_center = to_world.translation(); + // screen coordinate of volume center + Vec2i coor = CameraUtils::project(camera, volume_center); + Vec2d mouse_offset = coor.cast() - mouse_pos; + Vec2d mouse_offset_without_sla_shift = mouse_offset; + if (double sla_shift = gl_volume->get_sla_shift_z(); !is_approx(sla_shift, 0.)) { + Transform3d to_world_without_sla_move = instance->get_matrix() * volume->get_matrix(); + if (volume->text_configuration.has_value() && volume->text_configuration->fix_3mf_tr.has_value()) + to_world_without_sla_move = to_world_without_sla_move * (*volume->text_configuration->fix_3mf_tr); + // zero point of volume in world coordinate system + volume_center = to_world_without_sla_move.translation(); + // screen coordinate of volume center + coor = CameraUtils::project(camera, volume_center); + mouse_offset_without_sla_shift = coor.cast() - mouse_pos; + } Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); @@ -165,7 +141,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, std::optional start_angle; if (up_limit.has_value()) start_angle = Emboss::calc_up(world_tr, *up_limit); - surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle}; + surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle, true, mouse_offset_without_sla_shift}; // disable moving with object by mouse canvas.enable_moving(false); @@ -181,7 +157,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); - Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset; + Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset_without_sla_shift; std::optional hit = ray_from_camera( raycast_manager, offseted_mouse, camera, &surface_drag->condition); diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp index bb2600c28..48d6a33fe 100644 --- a/src/slic3r/GUI/SurfaceDrag.hpp +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -39,6 +39,9 @@ struct SurfaceDrag // Flag whether coordinate hit some volume bool exist_hit = true; + + // hold screen coor offset of cursor from object center without SLA shift + Vec2d mouse_offset_without_sla_shift; }; /// From c234b98db5c3aead681d7f5a8340774413071a6a Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Tue, 28 Mar 2023 21:00:07 +0200 Subject: [PATCH 191/201] Fix additional special case for ordering of extra perimeters --- src/libslic3r/PerimeterGenerator.cpp | 16 +++++++++++++--- src/libslic3r/SLAPrint.cpp | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index dc4c2c9cb..7236f66ff 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1019,13 +1019,23 @@ std::tuple, Polygons> generate_extra_perimeters_over overhang_region.end()); if (!overhang_region.empty()) { + // there is a special case, where the first (or last) generated overhang perimeter eats all anchor space. + // When this happens, the first overhang perimeter is also a closed loop, and needs special check + // instead of the following simple is_anchored lambda, which checks only the first and last point (not very useful on closed + // polyline) + bool first_overhang_is_closed_and_anchored = + (overhang_region.front().first_point() == overhang_region.front().last_point() && + !intersection_pl(overhang_region.front().polyline, optimized_lower_slices).empty()); + auto is_anchored = [&lower_layer_aabb_tree](const ExtrusionPath &path) { return lower_layer_aabb_tree.distance_from_lines(path.first_point()) <= 0 || lower_layer_aabb_tree.distance_from_lines(path.last_point()) <= 0; }; - std::reverse(overhang_region.begin(), overhang_region.end()); - auto first_unanchored = std::stable_partition(overhang_region.begin(), overhang_region.end(), is_anchored); - int index_of_first_unanchored = first_unanchored - overhang_region.begin(); + if (!first_overhang_is_closed_and_anchored) { + std::reverse(overhang_region.begin(), overhang_region.end()); + } + auto first_unanchored = std::stable_partition(overhang_region.begin(), overhang_region.end(), is_anchored); + int index_of_first_unanchored = first_unanchored - overhang_region.begin(); overhang_region = sort_extra_perimeters(overhang_region, index_of_first_unanchored, overhang_flow.scaled_spacing()); } } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 8238a266e..c4947851b 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -2,6 +2,7 @@ #include "SLAPrintSteps.hpp" #include "CSGMesh/CSGMeshCopy.hpp" #include "CSGMesh/PerformCSGMeshBooleans.hpp" +#include "format.hpp" #include "Geometry.hpp" #include "Thread.hpp" From cc5660ad8cf28a134cad5164061d890441655612 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 29 Mar 2023 10:45:11 +0200 Subject: [PATCH 192/201] Lock for rotation. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 39 +++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 2 ++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index ec2a74f96..a3cdb92a5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -223,6 +223,10 @@ enum class IconType : unsigned { system_selector, open_file, exclamation, + lock, + lock_bold, + unlock, + unlock_bold, // automatic calc of icon's count _count }; @@ -779,12 +783,12 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() ImGui::CalcTextSize(tr.boldness.c_str()).x, ImGui::CalcTextSize(tr.skew_ration.c_str()).x, ImGui::CalcTextSize(tr.from_surface.c_str()).x, - ImGui::CalcTextSize(tr.rotation.c_str()).x, + ImGui::CalcTextSize(tr.rotation.c_str()).x + cfg.icon_width + 2*space, ImGui::CalcTextSize(tr.keep_up.c_str()).x, ImGui::CalcTextSize(tr.collection.c_str()).x }); cfg.advanced_input_offset = max_advanced_text_width + 3 * space + cfg.indent; - + cfg.lock_offset = cfg.advanced_input_offset - (cfg.icon_width + space); // calculate window size float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y; float input_height = line_height_with_spacing + 2*style.FramePadding.y; @@ -806,9 +810,9 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() + 2 * (cfg.icon_width + space); cfg.minimal_window_size = ImVec2(window_width, window_height); - // 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera + // 8 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, textFaceToCamera // 4 = 1px for fix each edit image of drag float - float advance_height = input_height * 9 + 8; + float advance_height = input_height * 8 + 8; cfg.minimal_window_size_with_advance = ImVec2(cfg.minimal_window_size.x, cfg.minimal_window_size.y + advance_height); @@ -2652,8 +2656,8 @@ void GLGizmoEmboss::draw_height(bool use_inch) { float &value = m_style_manager.get_style().prop.size_in_mm; const EmbossStyle* stored_style = m_style_manager.get_stored_style(); - const float *stored = ((stored_style)? &stored_style->prop.size_in_mm : nullptr); - const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm"); + const float *stored = (stored_style != nullptr)? &stored_style->prop.size_in_mm : nullptr; + const char *size_format = use_inch ? "%.2f in" : "%.1f mm"; const std::string revert_text_size = _u8L("Revert text size."); const std::string& name = m_gui_cfg->translations.height; if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) @@ -2913,10 +2917,10 @@ void GLGizmoEmboss::draw_advanced() &stored_style->prop.distance : nullptr; m_imgui->disabled_begin(!allowe_surface_distance); - bool use_inch = wxGetApp().app_config->get_bool("use_inches"); const std::string undo_move_tooltip = _u8L("Undo translation"); const wxString move_tooltip = _L("Distance of the center of text from model surface"); bool is_moved = false; + bool use_inch = wxGetApp().app_config->get_bool("use_inches"); if (use_inch) { std::optional distance_inch; if (distance.has_value()) distance_inch = (*distance * ObjectManipulation::mm_to_in); @@ -2982,16 +2986,23 @@ void GLGizmoEmboss::draw_advanced() process(); } - ImGui::Text("%s", tr.keep_up.c_str()); - ImGui::SameLine(m_gui_cfg->advanced_input_offset); - if (ImGui::Checkbox("##keep_up", &m_keep_up)) { + // Keep up - lock button icon + ImGui::SameLine(m_gui_cfg->lock_offset); + const IconManager::Icon &icon = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::activable); + const IconManager::Icon &icon_hover = get_icon(m_icons, m_keep_up ? IconType::lock_bold : IconType::unlock_bold, IconState::activable); + const IconManager::Icon &icon_disable = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::disabled); + if (button(icon, icon_hover, icon_disable)) { + m_keep_up = !m_keep_up; if (m_keep_up) { // copy angle to volume m_volume->text_configuration->style.prop.angle = font_prop.angle; } } if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Lock the text's rotation when moving text along the object's surface.").c_str()); + ImGui::SetTooltip("%s", (m_keep_up? + _u8L("Unlock the text's up orientation when moving text along the object's surface."): + _u8L("Lock the text's up orientation when moving text along the object's surface.") + ).c_str()); // when more collection add selector if (ff.font_file->infos.size() > 1) { @@ -3270,7 +3281,11 @@ void GLGizmoEmboss::init_icons() "make_unbold.svg", "search.svg", "open.svg", - "exclamation.svg" + "exclamation.svg", + "lock_closed.svg", // lock, + "lock_closed_f.svg",// lock_bold, + "lock_open.svg", // unlock, + "lock_open_f.svg" // unlock_bold, }; assert(filenames.size() == static_cast(IconType::_count)); std::string path = resources_dir() + "/icons/"; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index a57def86e..d461425af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -182,6 +182,8 @@ private: float input_offset = 0.f; float advanced_input_offset = 0.f; + float lock_offset = 0.f; + ImVec2 text_size; // maximal size of face name image From eec51c67d32e18b079e72a4c9d4745a0d2f8f727 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 29 Mar 2023 10:49:35 +0200 Subject: [PATCH 193/201] Refactoring of PrintObject::discover_vertical_shells() for readability and efficiency. Also added an experiment of adding one more "ensuring" layer to support top / bottom surfaces, disabled with one_more_layer_below_top_bottom_surfaces --- src/libslic3r/PrintObject.cpp | 61 +++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 61067ed18..26b359c0e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1373,43 +1373,56 @@ void PrintObject::discover_vertical_shells() } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ polygons_append(holes, cache_top_botom_regions[idx_layer].holes); + auto combine_holes = [&holes](const Polygons &holes2) { + if (holes.empty() || holes2.empty()) + holes.clear(); + else + holes = intersection(holes, holes2); + }; + auto combine_shells = [&shell](const Polygons &shells2) { + if (shell.empty()) + shell = std::move(shells2); + else if (! shells2.empty()) { + polygons_append(shell, shells2); + // Running the union_ using the Clipper library piece by piece is cheaper + // than running the union_ all at once. + shell = union_(shell); + } + }; + static constexpr const bool one_more_layer_below_top_bottom_surfaces = false; if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - i < int(cache_top_botom_regions.size()) && - (i < int(idx_layer) + n_top_layers || - m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); + int i = int(idx_layer) + 1; + int itop = int(idx_layer) + n_top_layers; + for (; i < int(cache_top_botom_regions.size()) && + (i < itop || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); ++ i) { const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - if (! holes.empty()) - holes = intersection(holes, cache.holes); - if (! cache.top_surfaces.empty()) { - polygons_append(shell, cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper - // than running the union_ all at once. - shell = union_(shell); - } + combine_holes(cache.holes); + combine_shells(cache.top_surfaces); } + if (one_more_layer_below_top_bottom_surfaces) + if (i < int(cache_top_botom_regions.size()) && + (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_solid_min_thickness - EPSILON)) + combine_holes(cache_top_botom_regions[i].holes); } if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { // Gather bottom regions projected to this layer. coordf_t bottom_z = layer->bottom_z(); - for (int i = int(idx_layer) - 1; - i >= 0 && - (i > int(idx_layer) - n_bottom_layers || - bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); + int i = int(idx_layer) - 1; + int ibottom = int(idx_layer) - n_bottom_layers; + for (; i >= 0 && + (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); -- i) { const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - if (! holes.empty()) - holes = intersection(holes, cache.holes); - if (! cache.bottom_surfaces.empty()) { - polygons_append(shell, cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper - // than running the union_ all at once. - shell = union_(shell); - } + combine_holes(cache.holes); + combine_shells(cache.bottom_surfaces); } + if (one_more_layer_below_top_bottom_surfaces) + if (i >= 0 && + (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_solid_min_thickness - EPSILON)) + combine_holes(cache_top_botom_regions[i].holes); } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { From f71ea4b615c74772d807f2478296243ea8a1e85d Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 29 Mar 2023 10:54:03 +0200 Subject: [PATCH 194/201] MK4 resources --- .../profiles/PrusaResearch/MK4_thumbnail.png | Bin 0 -> 69761 bytes resources/profiles/PrusaResearch/mk4.svg | 101 ++++++++++++++++++ resources/profiles/PrusaResearch/mk4_bed.stl | Bin 0 -> 91884 bytes 3 files changed, 101 insertions(+) create mode 100644 resources/profiles/PrusaResearch/MK4_thumbnail.png create mode 100644 resources/profiles/PrusaResearch/mk4.svg create mode 100644 resources/profiles/PrusaResearch/mk4_bed.stl diff --git a/resources/profiles/PrusaResearch/MK4_thumbnail.png b/resources/profiles/PrusaResearch/MK4_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..2b729f930c5ab6a9891d8326cff3adfaa7e993ba GIT binary patch literal 69761 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℞Jvrj`T)MEYcGWKKKLm0t(12@ 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 IM6N<$f;Jq2^8f$< literal 0 HcmV?d00001 diff --git a/resources/profiles/PrusaResearch/mk4.svg b/resources/profiles/PrusaResearch/mk4.svg new file mode 100644 index 000000000..70a3b5eca --- /dev/null +++ b/resources/profiles/PrusaResearch/mk4.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/profiles/PrusaResearch/mk4_bed.stl b/resources/profiles/PrusaResearch/mk4_bed.stl new file mode 100644 index 0000000000000000000000000000000000000000..6aff36f0bcbda670a4d3ed645dcac8ff112d3462 GIT binary patch literal 91884 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 literal 0 HcmV?d00001 From 6f9f7ad47aae7eca8cd417c27dd30e79ab1abc34 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 29 Mar 2023 10:55:48 +0200 Subject: [PATCH 195/201] change to constexpr --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index d461425af..b66f42b0f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -256,7 +256,7 @@ private: std::vector bad = {}; // Configuration of font encoding - static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; + static constexpr wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; // Identify if preview texture exists GLuint texture_id = 0; From 0ed47616f9bad15997f5f12be362b6bacda6f488 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 29 Mar 2023 11:22:09 +0200 Subject: [PATCH 196/201] Added MK4 profiles. --- resources/profiles/PrusaResearch.idx | 3 + resources/profiles/PrusaResearch.ini | 2419 ++++++++++++++++++++++++-- 2 files changed, 2252 insertions(+), 170 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 77fdf5a83..a108d837f 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.0-alpha5 +1.9.0-alpha1 Added profiles for Original Prusa MK4. 1.9.0-alpha0 Updated output filename format. 1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. @@ -8,10 +9,12 @@ min_slic3r_version = 2.6.0-alpha1 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. min_slic3r_version = 2.5.1-rc0 +1.6.3 Added SLA materials. 1.6.2 Updated compatibility condition in some filament profiles (Prusa XL). 1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles. 1.6.0 Added Original Prusa XL profiles. Updated acceleration settings for Prusa MINI. Updated infill/perimeter overlap values. min_slic3r_version = 2.5.0-alpha0 +1.5.9 Added SLA materials. 1.5.8 Added filament profile for Prusament PETG Tungsten 75%. Updated FW version notification. 1.5.7 Added filament profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. 1.5.6 Updated FW version notification (MK2.5/MK3 family). Added filament profile for Kimya PEBA-S. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index f57a8110d..019465797 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.9.0-alpha0 +config_version = 1.9.0-alpha1 # 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% @@ -14,14 +14,14 @@ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # 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:XL] -name = Original Prusa XL -variants = 0.6; 0.25; 0.3; 0.4; 0.5; 0.8 +[printer_model:MK4] +name = Original Prusa MK4 +variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8 technology = FFF -family = XL -bed_model = xl_bed.stl -bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6 +family = MK4 +bed_model = mk4_bed.stl +bed_texture = mk4.svg +default_materials = Generic PLA @PG; Generic ABS @MK4; Generic PETG @PG; Prusament PLA @PG; Prusament PETG @PG; Prusament ASA @MK4; Prusament PC Blend @MK4; Prusament PC Blend Carbon Fiber @MK4; Prusament PVB @PG; Prusament PA11 Carbon Fiber @PG [printer_model:MINI] name = Original Prusa MINI && MINI+ @@ -68,6 +68,15 @@ bed_model = mk3_bed.stl bed_texture = mk3.svg default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA @MMU2; Prusament PETG @MMU2; Prusament ASA @MMU2; Verbatim BVOH @MMU2; Prusament PC Blend @MMU2; Prusament PC Blend Carbon Fiber @MMU2; Prusament PVB @MMU2 +[printer_model:XL] +name = Original Prusa XL +variants = 0.6; 0.25; 0.3; 0.4; 0.5; 0.8 +technology = FFF +family = XL +bed_model = xl_bed.stl +bed_texture = xl.svg +default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6 + [printer_model:MK2.5S] name = Original Prusa i3 MK2.5S variants = 0.4; 0.25; 0.6; 0.8 @@ -284,6 +293,30 @@ raft_first_layer_density = 80% ## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +[print:*MK4*] +inherits = *common* +single_extruder_multi_material_priming = 0 +travel_speed = 200 +travel_speed_z = 12 +fill_density = 15% +default_acceleration = 1000 +bridge_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +infill_anchor = 2 +perimeter_acceleration = 800 +first_layer_acceleration = 600 +fill_pattern = grid +skirts = 0 +extruder_clearance_height = 13 +extruder_clearance_radius = 45 +first_layer_speed = 20 +support_material_threshold = 45 +raft_first_layer_density = 80% +## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode + [print:*MK306*] inherits = *MK3* fill_pattern = gyroid @@ -363,6 +396,9 @@ infill_anchor = 1 perimeters = 3 brim_separation = 0 +[print:*0.25nozzleMK4*] +inherits = *0.25nozzleXL* + [print:*0.3nozzle*] external_perimeter_extrusion_width = 0.33 extrusion_width = 0.33 @@ -434,6 +470,9 @@ fill_density = 20% support_material_interface_spacing = 0.25 infill_anchor = 2.5 +[print:*0.6nozzleMK4*] +inherits = *0.6nozzleXL* + [print:*0.6nozzleMINI*] inherits = *0.6nozzleMK3* infill_extrusion_width = 0.68 @@ -502,6 +541,13 @@ support_material_style = snug raft_first_layer_expansion = 2 default_acceleration = 1250 infill_anchor = 2.5 +first_layer_acceleration = 500 + +[print:*0.8nozzleMK4*] +inherits = *0.8nozzleXL* +default_acceleration = 1000 +infill_acceleration = 2000 +first_layer_acceleration = 600 [print:*soluble_support*] overhangs = 1 @@ -1848,7 +1894,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.25 [print:0.05mm ULTRADETAIL @XL 0.3] inherits = *0.05mm*; *XL*; *0.3nozzle* -top_solid_layers = 12 +top_solid_layers = 14 bottom_solid_layers = 9 support_material_contact_distance = 0.07 raft_contact_distance = 0.07 @@ -1987,6 +2033,8 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.3 [print:0.07mm ULTRADETAIL @XL 0.4] inherits = *0.07mm*; *XL* +thick_bridges = 1 +bridge_flow_ratio = 0.6 top_infill_extrusion_width = 0.4 first_layer_extrusion_width = 0.45 perimeter_extrusion_width = 0.4 @@ -2173,13 +2221,13 @@ bridge_acceleration = 1000 first_layer_acceleration = 600 default_acceleration = 1250 max_print_speed = 200 -external_perimeter_extrusion_width = 0.55 -extrusion_width = 0.55 +external_perimeter_extrusion_width = 0.5 +extrusion_width = 0.5 first_layer_extrusion_width = 0.5 -infill_extrusion_width = 0.55 -perimeter_extrusion_width = 0.55 -solid_infill_extrusion_width = 0.55 -top_infill_extrusion_width = 0.42 +infill_extrusion_width = 0.5 +perimeter_extrusion_width = 0.5 +solid_infill_extrusion_width = 0.5 +top_infill_extrusion_width = 0.45 support_material_extrusion_width = 0.38 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 @@ -2311,6 +2359,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.5 [print:0.15mm DETAIL @XL 0.6] inherits = *0.15mm*; *XL*; *0.6nozzleXL* +fill_pattern = gyroid perimeter_speed = 45 external_perimeter_speed = 30 small_perimeter_speed = 30 @@ -2345,6 +2394,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 [print:0.20mm DETAIL @XL 0.6] inherits = *0.20mm*; *XL*; *0.6nozzleXL* +fill_pattern = gyroid perimeter_speed = 50 external_perimeter_speed = 35 small_perimeter_speed = 30 @@ -2425,7 +2475,7 @@ external_perimeter_acceleration = 1000 perimeter_acceleration = 1200 solid_infill_acceleration = 2000 infill_acceleration = 3000 -top_solid_layers = 4 +top_solid_layers = 5 bottom_solid_layers = 4 default_acceleration = 1250 dynamic_overhang_speeds = 35,20,15,15 @@ -2603,6 +2653,910 @@ top_solid_infill_acceleration = 800 solid_infill_acceleration = 2000 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +## MK4 ## + +## MK4 - 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @MK4 0.25] +inherits = *0.05mm*; *MK4*; *0.25nozzleMK4* +support_material_contact_distance = 0.07 +raft_contact_distance = 0.1 +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 40 +solid_infill_speed = 40 +top_solid_infill_speed = 25 +support_material_speed = 30 +support_material_interface_speed = 80% +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 800 +infill_acceleration = 800 +bridge_acceleration = 300 +first_layer_acceleration = 600 +default_acceleration = 800 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.25 + +[print:0.07mm ULTRADETAIL @MK4 0.25] +inherits = *0.07mm*; *MK4*; *0.25nozzleMK4* +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 45 +solid_infill_speed = 45 +top_solid_infill_speed = 35 +support_material_speed = 30 +support_material_interface_speed = 80% +support_material_contact_distance = 0.07 +gap_fill_speed = 25 +bridge_speed = 20 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 500 +solid_infill_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 300 +first_layer_acceleration = 600 +default_acceleration = 800 +max_print_speed = 200 +gcode_resolution = 0.006 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.25 + +[print:0.10mm QUALITY @MK4 0.25] +inherits = *0.10mm*; *MK4*; *0.25nozzleMK4* +perimeter_speed = 35 +external_perimeter_speed = 20 +small_perimeter_speed = 25 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 30 +support_material_speed = 40 +support_material_interface_speed = 85% +support_material_contact_distance = 0.07 +gap_fill_speed = 30 +bridge_speed = 20 +external_perimeter_acceleration = 400 +perimeter_acceleration = 500 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 800 +infill_acceleration = 1200 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.25 + +[print:0.12mm QUALITY @MK4 0.25] +inherits = *0.12mm*; *MK4*; *0.25nozzleMK4* +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 30 +support_material_speed = 50 +support_material_interface_speed = 80% +support_material_contact_distance = 0.08 +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 500 +perimeter_acceleration = 500 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 1000 +infill_acceleration = 1200 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +perimeter_extrusion_width = 0.27 +external_perimeter_extrusion_width = 0.27 +infill_extrusion_width = 0.27 +solid_infill_extrusion_width = 0.27 +top_infill_extrusion_width = 0.25 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.25 + +[print:0.15mm SPEED @MK4 0.25] +inherits = *0.15mm*; *MK4*; *0.25nozzleMK4* +perimeter_speed = 35 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 80 +solid_infill_speed = 60 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +support_material_contact_distance = 0.08 +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 500 +perimeter_acceleration = 500 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 1000 +infill_acceleration = 1200 +bridge_acceleration = 500 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.3 +perimeter_extrusion_width = 0.27 +external_perimeter_extrusion_width = 0.27 +infill_extrusion_width = 0.27 +solid_infill_extrusion_width = 0.27 +top_infill_extrusion_width = 0.25 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.25 + +## MK4 - 0.3mm nozzle + +[print:0.05mm ULTRADETAIL @MK4 0.3] +inherits = *0.05mm*; *MK4*; *0.3nozzle* +top_solid_layers = 14 +bottom_solid_layers = 9 +support_material_contact_distance = 0.07 +raft_contact_distance = 0.07 +perimeter_speed = 25 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 45 +solid_infill_speed = 45 +top_solid_infill_speed = 35 +support_material_speed = 40 +support_material_interface_speed = 80% +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 600 +solid_infill_acceleration = 800 +infill_acceleration = 800 +bridge_acceleration = 500 +default_acceleration = 800 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.3 + +[print:0.08mm DETAIL @MK4 0.3] +inherits = *0.07mm*; *MK4*; *0.3nozzle* +layer_height = 0.08 +support_material_contact_distance = 0.08 +raft_contact_distance = 0.08 +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 35 +support_material_speed = 40 +support_material_interface_speed = 80% +gap_fill_speed = 25 +bridge_speed = 20 +external_perimeter_acceleration = 500 +perimeter_acceleration = 600 +top_solid_infill_acceleration = 700 +solid_infill_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +perimeters = 3 +gcode_resolution = 0.006 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.3 + +[print:0.12mm QUALITY @MK4 0.3] +inherits = *0.12mm*; *MK4*; *0.3nozzle* +support_material_contact_distance = 0.1 +raft_contact_distance = 0.1 +perimeter_speed = 35 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 80 +solid_infill_speed = 80 +top_solid_infill_speed = 30 +support_material_speed = 45 +support_material_interface_speed = 85% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 600 +perimeter_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1200 +infill_acceleration = 1500 +bridge_acceleration = 800 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.3 + +[print:0.16mm SPEED @MK4 0.3] +inherits = *0.16mm*; *MK4*; *0.3nozzle* +support_material_contact_distance = 0.15 +raft_contact_distance = 0.15 +perimeter_speed = 50 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 90 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_speed = 45 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 600 +perimeter_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1250 +bridge_acceleration = 800 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.3 + +[print:0.20mm DRAFT @MK4 0.3] +inherits = *0.20mm*; *MK4*; *0.3nozzle* +support_material_contact_distance = 0.18 +raft_contact_distance = 0.18 +perimeter_speed = 50 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +infill_speed = 90 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 700 +perimeter_acceleration = 800 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1250 +bridge_acceleration = 800 +default_acceleration = 1250 +max_print_speed = 200 +first_layer_extrusion_width = 0.4 +perimeter_extrusion_width = 0.35 +external_perimeter_extrusion_width = 0.35 +infill_extrusion_width = 0.35 +solid_infill_extrusion_width = 0.35 +top_infill_extrusion_width = 0.3 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.3 + +## MK4 - 0.4mm nozzle + +[print:0.05mm ULTRADETAIL @MK4 0.4] +inherits = *0.05mm*; *MK4* +thick_bridges = 1 +bridge_flow_ratio = 0.6 +top_infill_extrusion_width = 0.4 +first_layer_extrusion_width = 0.45 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 +infill_extrusion_width = 0.4 +solid_infill_extrusion_width = 0.4 +perimeters = 3 +support_material_contact_distance = 0.1 +raft_contact_distance = 0.1 +perimeter_speed = 30 +external_perimeter_speed = 20 +small_perimeter_speed = 20 +infill_speed = 40 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_style = snug +support_material_interface_layers = 0 +support_material_speed = 30 +support_material_interface_speed = 80% +support_material_spacing = 1.5 +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 500 +solid_infill_acceleration = 700 +infill_acceleration = 800 +bridge_acceleration = 300 +default_acceleration = 800 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.07mm ULTRADETAIL @MK4 0.4] +inherits = *0.07mm*; *MK4* +thick_bridges = 1 +bridge_flow_ratio = 0.6 +top_infill_extrusion_width = 0.4 +first_layer_extrusion_width = 0.45 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 +infill_extrusion_width = 0.4 +solid_infill_extrusion_width = 0.4 +perimeters = 3 +support_material_contact_distance = 0.1 +raft_contact_distance = 0.1 +perimeter_speed = 35 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 45 +solid_infill_speed = 45 +top_solid_infill_speed = 30 +support_material_style = snug +support_material_interface_layers = 0 +support_material_speed = 35 +support_material_interface_speed = 80% +support_material_spacing = 1.5 +gap_fill_speed = 25 +gcode_resolution = 0.006 +external_perimeter_acceleration = 300 +perimeter_acceleration = 300 +top_solid_infill_acceleration = 500 +solid_infill_acceleration = 800 +infill_acceleration = 800 +bridge_acceleration = 300 +first_layer_acceleration = 600 +default_acceleration = 800 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.10mm DETAIL @MK4 0.4] +inherits = *0.10mm*; *MK4* +support_material_contact_distance = 0.17 +raft_contact_distance = 0.15 +perimeter_speed = 45 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 80 +solid_infill_speed = 80 +top_solid_infill_speed = 35 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 40 +support_material_interface_speed = 85% +support_material_xy_spacing = 80% +gap_fill_speed = 40 +bridge_speed = 20 +external_perimeter_acceleration = 500 +perimeter_acceleration = 700 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1250 +infill_acceleration = 1500 +bridge_acceleration = 700 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +perimeter_extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.4 +infill_extrusion_width = 0.4 +solid_infill_extrusion_width = 0.4 +top_infill_extrusion_width = 0.4 +perimeters = 3 +gcode_resolution = 0.006 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.15mm QUALITY @MK4 0.4] +inherits = *0.15mm*; *MK4* +perimeter_speed = 45 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 90 +solid_infill_speed = 90 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.15 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 45 +support_material_interface_speed = 80% +support_material_xy_spacing = 80% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 700 +perimeter_acceleration = 900 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +bridge_acceleration = 1000 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +support_material_extrusion_width = 0.37 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.15mm SPEED @MK4 0.4] +inherits = *0.15mm*; *MK4* +perimeter_speed = 70 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 200 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.15 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 45 +support_material_interface_speed = 80% +support_material_xy_spacing = 80% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +bridge_acceleration = 1000 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +support_material_extrusion_width = 0.37 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.20mm QUALITY @MK4 0.4] +inherits = *0.20mm*; *MK4* +perimeter_speed = 45 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 90 +solid_infill_speed = 90 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 5 +support_material_xy_spacing = 80% +support_material_speed = 50 +support_material_interface_speed = 70% +gap_fill_speed = 40 +bridge_speed = 25 +external_perimeter_acceleration = 700 +perimeter_acceleration = 900 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +bridge_acceleration = 1000 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +gcode_resolution = 0.008 +support_material_extrusion_width = 0.37 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.20mm SPEED @MK4 0.4] +inherits = *0.20mm*; *MK4* +perimeter_speed = 70 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 200 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 50 +support_material_interface_speed = 70% +support_material_xy_spacing = 80% +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +support_material_extrusion_width = 0.37 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +[print:0.30mm DRAFT @MK4 0.4] +inherits = *0.30mm*; *MK4* +bottom_solid_layers = 3 +perimeter_speed = 70 +external_perimeter_speed = 40 +small_perimeter_speed = 40 +infill_speed = 95 +solid_infill_speed = 85 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.2 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 5 +support_material_speed = 60 +support_material_interface_speed = 70% +support_material_xy_spacing = 80% +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1100 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1700 +infill_acceleration = 2000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +external_perimeter_extrusion_width = 0.5 +extrusion_width = 0.5 +first_layer_extrusion_width = 0.5 +infill_extrusion_width = 0.5 +perimeter_extrusion_width = 0.5 +solid_infill_extrusion_width = 0.5 +top_infill_extrusion_width = 0.45 +support_material_extrusion_width = 0.38 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 + +## MK4 - 0.5mm nozzle + +[print:0.10mm DETAIL @MK4 0.5] +inherits = *0.10mm*; *MK4*; *0.5nozzle* +fill_pattern = gyroid +perimeter_speed = 40 +external_perimeter_speed = 25 +small_perimeter_speed = 25 +infill_speed = 80 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 85% +support_material_style = snug +support_material_interface_layers = 4 +gap_fill_speed = 40 +bridge_speed = 30 +external_perimeter_acceleration = 700 +perimeter_acceleration = 800 +solid_infill_acceleration = 1200 +infill_acceleration = 1500 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +first_layer_extrusion_width = 0.5 +perimeter_extrusion_width = 0.5 +external_perimeter_extrusion_width = 0.5 +infill_extrusion_width = 0.5 +solid_infill_extrusion_width = 0.5 +top_infill_extrusion_width = 0.45 +perimeters = 2 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.5 + +[print:0.15mm QUALITY @MK4 0.5] +inherits = *0.15mm*; *MK4*; *0.5nozzle* +fill_pattern = gyroid +perimeter_speed = 45 +external_perimeter_speed = 30 +small_perimeter_speed = 25 +infill_speed = 120 +solid_infill_speed = 120 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 70% +support_material_style = snug +support_material_interface_layers = 4 +gap_fill_speed = 40 +bridge_speed = 30 +external_perimeter_acceleration = 800 +perimeter_acceleration = 900 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +bridge_acceleration = 1000 +first_layer_acceleration = 600 +default_acceleration = 1000 +max_print_speed = 200 +gcode_resolution = 0.008 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.5 + +[print:0.20mm QUALITY @MK4 0.5] +inherits = 0.15mm QUALITY @MK4 0.5; *0.20mm*; *MK4*; *0.5nozzle* +perimeter_speed = 50 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +gcode_resolution = 0.01 +support_material_interface_layers = 4 +infill_speed = 200 +solid_infill_speed = 120 +support_material_speed = 50 +support_material_interface_speed = 70% +external_perimeter_acceleration = 800 +perimeter_acceleration = 900 +infill_acceleration = 2000 +default_acceleration = 1000 +max_print_speed = 200 + +[print:0.25mm SPEED @MK4 0.5] +inherits = *0.25mm*; *MK4*; *0.5nozzle* +bottom_solid_layers = 3 +perimeter_speed = 70 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 75% +support_material_style = snug +support_material_interface_layers = 4 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 900 +perimeter_acceleration = 1000 +infill_acceleration = 2000 +bridge_acceleration = 800 +default_acceleration = 1000 +max_print_speed = 200 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.5 + +[print:0.32mm DRAFT @MK4 0.5] +inherits = *0.32mm*; *MK4*; *0.5nozzle* +bottom_solid_layers = 3 +perimeter_speed = 70 +external_perimeter_speed = 45 +small_perimeter_speed = 40 +infill_speed = 200 +solid_infill_speed = 100 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 75% +support_material_style = snug +support_material_interface_layers = 4 +support_material_contact_distance = 0.3 +support_material_extrusion_width = 0.42 +raft_contact_distance = 0.3 +gap_fill_speed = 45 +bridge_speed = 25 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1000 +infill_acceleration = 2000 +bridge_acceleration = 1000 +default_acceleration = 1000 +max_print_speed = 200 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.5 + +## MK4 - 0.6mm nozzle + +[print:0.15mm DETAIL @MK4 0.6] +inherits = *0.15mm*; *MK4*; *0.6nozzleMK4* +fill_pattern = gyroid +perimeter_speed = 45 +external_perimeter_speed = 30 +small_perimeter_speed = 30 +infill_speed = 100 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.22 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.65 +external_perimeter_extrusion_width = 0.6 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.6 +perimeter_extrusion_width = 0.6 +solid_infill_extrusion_width = 0.6 +top_infill_extrusion_width = 0.5 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 800 +infill_acceleration = 2000 +bridge_acceleration = 800 +default_acceleration = 1000 +bridge_flow_ratio = 1 +max_print_speed = 200 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[print:0.20mm DETAIL @MK4 0.6] +inherits = *0.20mm*; *MK4*; *0.6nozzleMK4* +fill_pattern = gyroid +perimeter_speed = 50 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +infill_speed = 120 +solid_infill_speed = 80 +support_material_contact_distance = 0.22 +raft_contact_distance = 0.2 +support_material_style = snug +support_material_interface_layers = 4 +top_solid_infill_speed = 40 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.65 +external_perimeter_extrusion_width = 0.6 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.6 +perimeter_extrusion_width = 0.6 +solid_infill_extrusion_width = 0.6 +top_infill_extrusion_width = 0.5 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 900 +infill_acceleration = 2000 +default_acceleration = 1000 +bridge_flow_ratio = 1 +max_print_speed = 200 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[print:0.25mm QUALITY @MK4 0.6] +inherits = *0.25mm*; *MK4*; *0.6nozzleMK4* +perimeter_speed = 60 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +infill_speed = 200 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 75% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.65 +top_infill_extrusion_width = 0.55 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +infill_acceleration = 2000 +bridge_acceleration = 1000 +default_acceleration = 1000 +bridge_flow_ratio = 1 +top_solid_layers = 5 +bottom_solid_layers = 4 +max_print_speed = 200 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[print:0.32mm SPEED @MK4 0.6] +inherits = *0.32mm*; *MK4*; *0.6nozzleMK4* +perimeter_speed = 65 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 75% +gap_fill_speed = 50 +bridge_speed = 25 +extrusion_width = 0.68 +external_perimeter_extrusion_width = 0.68 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +top_infill_extrusion_width = 0.55 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +first_layer_acceleration = 600 +default_acceleration = 1000 +bridge_flow_ratio = 0.95 +max_print_speed = 200 +bottom_solid_layers = 3 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[print:0.40mm DRAFT @MK4 0.6] +inherits = *0.40mm*; *MK4*; *0.6nozzleMK4* +perimeter_speed = 60 +external_perimeter_speed = 40 +small_perimeter_speed = 35 +infill_speed = 200 +solid_infill_speed = 55 +top_solid_infill_speed = 40 +support_material_contact_distance = 0.25 +raft_contact_distance = 0.25 +support_material_style = snug +support_material_interface_layers = 4 +support_material_speed = 50 +support_material_interface_speed = 80% +gap_fill_speed = 40 +bridge_speed = 25 +extrusion_width = 0.68 +external_perimeter_extrusion_width = 0.68 +first_layer_extrusion_width = 0.65 +infill_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.5 +external_perimeter_acceleration = 900 +perimeter_acceleration = 1000 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +default_acceleration = 1500 +bridge_flow_ratio = 0.95 +dynamic_overhang_speeds = 30,20,15,15 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +## MK4 - 0.8mm nozzle + +[print:0.30mm DETAIL @MK4 0.8] +inherits = *0.30mm*; *MK4*; *0.8nozzleMK4* +perimeter_speed = 45 +external_perimeter_speed = 30 +small_perimeter_speed = 30 +infill_speed = 70 +solid_infill_speed = 50 +support_material_speed = 40 +support_material_interface_speed = 90% +top_solid_infill_speed = 35 +bridge_speed = 22 +gap_fill_speed = 30 +top_infill_extrusion_width = 0.75 +support_material_extrusion_width = 0.7 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 + +[print:0.40mm QUALITY @MK4 0.8] +inherits = *0.40mm*; *MK4*; *0.8nozzleMK4* +perimeter_speed = 45 +external_perimeter_speed = 35 +small_perimeter_speed = 30 +infill_speed = 65 +solid_infill_speed = 45 +top_solid_infill_speed = 35 +support_material_speed = 40 +support_material_interface_speed = 90% +bridge_speed = 22 +gap_fill_speed = 30 +top_infill_extrusion_width = 0.8 +support_material_extrusion_width = 0.7 +external_perimeter_acceleration = 800 +perimeter_acceleration = 1000 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 + +[print:0.55mm DRAFT @MK4 0.8] +inherits = *MK4*; *0.8nozzleMK4* +layer_height = 0.55 +top_solid_layers = 4 +bottom_solid_layers = 3 +perimeter_speed = 40 +external_perimeter_speed = 30 +small_perimeter_speed = 30 +infill_speed = 55 +solid_infill_speed = 35 +top_solid_infill_speed = 30 +support_material_speed = 35 +support_material_interface_speed = 90% +bridge_speed = 22 +gap_fill_speed = 30 +top_infill_extrusion_width = 0.8 +support_material_extrusion_width = 0.7 +perimeter_extrusion_width = 1 +external_perimeter_extrusion_width = 1 +external_perimeter_acceleration = 1000 +perimeter_acceleration = 1000 +bridge_acceleration = 1000 +top_solid_infill_acceleration = 800 +solid_infill_acceleration = 1500 +infill_acceleration = 2000 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 + # XXXXXXxxXXXXXXXXXXXXXX # XXX--- filament ---XXX # XXXXXXXXxxXXXXXXXXXXXX @@ -2611,7 +3565,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 cooling = 1 compatible_printers = # For now, all but selected filaments are disabled for the MMU 2.0 -compatible_printers_condition = ! single_extruder_multi_material and printer_model!="XL" +compatible_printers_condition = ! single_extruder_multi_material and printer_notes!~/.*PG.*/ end_filament_gcode = "; Filament-specific end gcode" extrusion_multiplier = 1 filament_loading_speed = 28 @@ -2654,11 +3608,11 @@ min_fan_speed = 100 temperature = 210 slowdown_below_layer_time = 10 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = ! single_extruder_multi_material and printer_model!="XL" +compatible_printers_condition = ! single_extruder_multi_material and printer_notes!~/.*PG.*/ [filament:*PLAPG*] start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.06{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.08{elsif nozzle_diameter[0]==0.35}0.07{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 slowdown_below_layer_time = 8 filament_cooling_final_speed = 2 filament_cooling_initial_speed = 3 @@ -2675,13 +3629,13 @@ filament_unloading_speed_start = 100 [filament:*PLA06PG*] inherits = *PLAPG* -compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.6 filament_max_volumetric_speed = 15.5 slowdown_below_layer_time = 10 [filament:*PLA08PG*] inherits = *PLAPG* -compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.8 filament_max_volumetric_speed = 19 slowdown_below_layer_time = 18 @@ -2704,15 +3658,15 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 240 filament_retract_length = 1 filament_retract_lift = 0.2 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:*PET06*] inherits = *PET* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) filament_max_volumetric_speed = 15 [filament:*PETPG*] -compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 filament_max_volumetric_speed = 10 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.08{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.1{elsif nozzle_diameter[0]==0.35}0.09{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp" filament_cooling_final_speed = 1 @@ -2733,24 +3687,25 @@ slowdown_below_layer_time = 9 [filament:*PET06PG*] inherits = *PETPG* -compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.6 filament_max_volumetric_speed = 17 slowdown_below_layer_time = 12 +filament_retract_length = 0.9 [filament:*PET08PG*] inherits = *PETPG* -compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.8 filament_max_volumetric_speed = 22 slowdown_below_layer_time = 18 +filament_retract_length = 0.8 [filament:*04PLUS*] -compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! single_extruder_multi_material and printer_model!="XL" +compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! single_extruder_multi_material and printer_notes!~/.*PG.*/ [filament:*04PLUSPG*] -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL" +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ [filament:*PETMMU1*] -# inherits = *PET* filament_retract_length = nil filament_retract_speed = nil filament_retract_lift = 0.2 @@ -2834,7 +3789,7 @@ max_fan_speed = 30 min_fan_speed = 20 temperature = 255 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*ABSC*] inherits = *common* @@ -2856,7 +3811,7 @@ min_fan_speed = 15 min_print_speed = 15 temperature = 255 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*ABSPG*] compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 @@ -2884,6 +3839,21 @@ filament_max_volumetric_speed = 18 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 slowdown_below_layer_time = 25 +[filament:*ABSMK4*] +inherits = *ABSPG* +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 + +[filament:*ABS06MK4*] +inherits = *ABSMK4* +filament_max_volumetric_speed = 15 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[filament:*ABS08MK4*] +inherits = *ABSMK4* +filament_max_volumetric_speed = 18 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 +slowdown_below_layer_time = 25 + [filament:*PCPG*] inherits = *ABSPG* filament_max_volumetric_speed = 8 @@ -2902,6 +3872,22 @@ inherits = *PCPG* filament_max_volumetric_speed = 20 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +[filament:*PCMK4*] +inherits = *ABSMK4* +filament_max_volumetric_speed = 8 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S45 ; set heatbreak target temp\n" +## idle_temperature = 170 + +[filament:*PC06MK4*] +inherits = *PCMK4* +filament_max_volumetric_speed = 14 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[filament:*PC08MK4*] +inherits = *PCMK4* +filament_max_volumetric_speed = 20 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 + [filament:*PAPG*] inherits = *ABSPG* filament_max_volumetric_speed = 5 @@ -2910,21 +3896,37 @@ bed_temperature = 105 ## idle_temperature = 170 [filament:*PA06PG*] -inherits = *PCPG* +inherits = *PAPG* filament_max_volumetric_speed = 7 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 [filament:*PA08PG*] -inherits = *PCPG* +inherits = *PAPG* filament_max_volumetric_speed = 10 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +[filament:*PAMK4*] +inherits = *ABSMK4* +filament_max_volumetric_speed = 5 +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S45 ; set heatbreak target temp\n" +## idle_temperature = 170 + +[filament:*PA06MK4*] +inherits = *PAMK4* +filament_max_volumetric_speed = 7 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[filament:*PA08MK4*] +inherits = *PAMK4* +filament_max_volumetric_speed = 10 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 + [filament:*FLEX*] inherits = *common* bed_temperature = 50 bridge_fan_speed = 80 # For now, all but selected filaments are disabled for the MMU 2.0 -compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) cooling = 0 disable_fan_first_layers = 3 extrusion_multiplier = 1.15 @@ -2945,7 +3947,7 @@ filament_retract_lift = 0 filament_wipe = 0 [filament:*FLEXPG*] -filament_max_volumetric_speed = 4 +filament_max_volumetric_speed = 3.5 filament_retract_speed = 60 filament_deretract_speed = 20 filament_retract_before_travel = 2 @@ -2955,14 +3957,33 @@ start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak tar [filament:*FLEX06PG*] inherits = *FLEXPG* -filament_max_volumetric_speed = 6.5 +filament_max_volumetric_speed = 4.5 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 [filament:*FLEX08PG*] inherits = *FLEXPG* -filament_max_volumetric_speed = 9 +filament_max_volumetric_speed = 8 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 +[filament:*FLEXMK4*] +filament_max_volumetric_speed = 4 +filament_retract_speed = 60 +filament_deretract_speed = 20 +filament_retract_before_travel = 2 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +## idle_temperature = 170 +start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" + +[filament:*FLEX06MK4*] +inherits = *FLEXMK4* +filament_max_volumetric_speed = 6.5 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[filament:*FLEX08MK4*] +inherits = *FLEXMK4* +filament_max_volumetric_speed = 9 +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 + [filament:ColorFabb bronzeFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -3049,12 +4070,27 @@ temperature = 270 inherits = ColorFabb HT; *PETPG* first_layer_bed_temperature = 100 bed_temperature = 105 +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:ColorFabb HT @PG 0.6] inherits = ColorFabb HT @PG; *PET06PG* +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6 [filament:ColorFabb HT @PG 0.8] inherits = ColorFabb HT @PG; *PET08PG* +compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 + +[filament:ColorFabb HT @MK4] +inherits = ColorFabb HT; *PETPG* +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 + +[filament:ColorFabb HT @MK4 0.6] +inherits = ColorFabb HT @MK4; *PET06PG* +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6 + +[filament:ColorFabb HT @MK4 0.8] +inherits = ColorFabb HT @MK4; *PET08PG* +compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 [filament:ColorFabb PLA-PHA] inherits = *PLA* @@ -3160,7 +4196,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 260 filament_retract_length = nil filament_retract_lift = 0.4 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:ColorFabb XT-CF20 @PG] inherits = ColorFabb XT-CF20; *PETPG*; *04PLUSPG* @@ -3217,19 +4253,32 @@ min_fan_speed = 20 temperature = 260 filament_retract_length = nil filament_retract_lift = 0 -compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and printer_model!="XL" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) [filament:ColorFabb nGen flex @PG] inherits = ColorFabb nGen flex; *FLEXPG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 6 filament_retract_length = 2.5 [filament:ColorFabb nGen flex @PG 0.6] inherits = ColorFabb nGen flex; *FLEX06PG* -filament_max_volumetric_speed = 9 +filament_max_volumetric_speed = 7 [filament:ColorFabb nGen flex @PG 0.8] inherits = ColorFabb nGen flex; *FLEX08PG* +filament_max_volumetric_speed = 10 + +[filament:ColorFabb nGen flex @MK4] +inherits = ColorFabb nGen flex; *FLEXMK4* +filament_max_volumetric_speed = 7 +filament_retract_length = 2.5 + +[filament:ColorFabb nGen flex @MK4 0.6] +inherits = ColorFabb nGen flex; *FLEX06MK4* +filament_max_volumetric_speed = 9 + +[filament:ColorFabb nGen flex @MK4 0.8] +inherits = ColorFabb nGen flex; *FLEX08MK4* filament_max_volumetric_speed = 12 [filament:Kimya PETG Carbon] @@ -3246,7 +4295,7 @@ temperature = 240 filament_retract_length = nil filament_retract_lift = 0.3 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.06{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Kimya PETG Carbon @PG] inherits = Kimya PETG Carbon; *PETPG*; *04PLUSPG* @@ -3268,12 +4317,13 @@ filament_colour = #804040 filament_max_volumetric_speed = 6 first_layer_temperature = 260 temperature = 260 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Kimya ABS Carbon @PG] inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG* bed_temperature = 105 filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL" [filament:Kimya ABS Carbon @PG 0.6] inherits = Kimya ABS Carbon @PG; *ABS06PG* @@ -3283,6 +4333,19 @@ filament_max_volumetric_speed = 10 inherits = Kimya ABS Carbon @PG; *ABS08PG* filament_max_volumetric_speed = 14 +[filament:Kimya ABS Carbon @MK4] +inherits = Kimya ABS Carbon; *ABSMK4* +filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="MK4" + +[filament:Kimya ABS Carbon @MK4 0.6] +inherits = Kimya ABS Carbon @MK4; *ABS06MK4* +filament_max_volumetric_speed = 10 + +[filament:Kimya ABS Carbon @MK4 0.8] +inherits = Kimya ABS Carbon @MK4; *ABS08MK4* +filament_max_volumetric_speed = 14 + [filament:Kimya ABS Kevlar] inherits = Kimya ABS Carbon filament_vendor = Kimya @@ -3291,6 +4354,7 @@ filament_density = 1.037 [filament:Kimya ABS Kevlar @PG] inherits = Kimya ABS Kevlar; *ABSPG*; *04PLUSPG* bed_temperature = 105 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL" [filament:Kimya ABS Kevlar @PG 0.6] inherits = Kimya ABS Kevlar @PG; *ABS06PG* @@ -3300,6 +4364,18 @@ filament_max_volumetric_speed = 10 inherits = Kimya ABS Kevlar @PG; *ABS08PG* filament_max_volumetric_speed = 14 +[filament:Kimya ABS Kevlar @MK4] +inherits = Kimya ABS Kevlar; *ABSMK4*; *04PLUSPG* +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="MK4" + +[filament:Kimya ABS Kevlar @MK4 0.6] +inherits = Kimya ABS Kevlar @MK4; *ABS06MK4* +filament_max_volumetric_speed = 10 + +[filament:Kimya ABS Kevlar @MK4 0.8] +inherits = Kimya ABS Kevlar @MK4; *ABS08MK4* +filament_max_volumetric_speed = 14 + [filament:Kimya PEBA-S] inherits = *PET* filament_vendor = Kimya @@ -3312,7 +4388,7 @@ filament_max_volumetric_speed = 6.5 filament_type = PEBA min_fan_speed = 30 max_fan_speed = 30 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:Kimya PEBA-S @PG] inherits = Kimya PEBA-S; *PETPG* @@ -3387,6 +4463,15 @@ inherits = Fillamentum ABS @PG; *ABS06PG* [filament:Fillamentum ABS @PG 0.8] inherits = Fillamentum ABS @PG; *ABS08PG* +[filament:Fillamentum ABS @MK4] +inherits = Fillamentum ABS; *ABSMK4* + +[filament:Fillamentum ABS @MK4 0.6] +inherits = Fillamentum ABS @MK4; *ABS06MK4* + +[filament:Fillamentum ABS @MK4 0.8] +inherits = Fillamentum ABS @MK4; *ABS08MK4* + [filament:Fillamentum ASA] inherits = *ABS* filament_vendor = Fillamentum @@ -3413,6 +4498,15 @@ inherits = Fillamentum ASA @PG; *ABS06PG* [filament:Fillamentum ASA @PG 0.8] inherits = Fillamentum ASA @PG; *ABS08PG* +[filament:Fillamentum ASA @MK4] +inherits = Fillamentum ASA; *ABSMK4* + +[filament:Fillamentum ASA @MK4 0.6] +inherits = Fillamentum ASA @MK4; *ABS06MK4* + +[filament:Fillamentum ASA @MK4 0.8] +inherits = Fillamentum ASA @MK4; *ABS08MK4* + [filament:Prusament ASA] inherits = *ABS* filament_vendor = Prusa Polymers @@ -3434,7 +4528,7 @@ disable_fan_first_layers = 4 filament_type = ASA filament_colour = #FFF2EC start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:Prusament ASA @PG] inherits = Prusament ASA; *ABSPG* @@ -3449,6 +4543,17 @@ inherits = Prusament ASA @PG; *ABS08PG* first_layer_temperature = 265 temperature = 265 +[filament:Prusament ASA @MK4] +inherits = Prusament ASA; *ABSMK4* + +[filament:Prusament ASA @MK4 0.6] +inherits = Prusament ASA @MK4; *ABS06MK4* + +[filament:Prusament ASA @MK4 0.8] +inherits = Prusament ASA @MK4; *ABS08MK4* +first_layer_temperature = 265 +temperature = 265 + [filament:Prusament PC Blend] inherits = *ABS* filament_vendor = Prusa Polymers @@ -3472,7 +4577,7 @@ filament_type = PC filament_colour = #DEE0E6 filament_max_volumetric_speed = 8 filament_retract_lift = 0.2 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" [filament:Prusament PC Blend @PG] @@ -3487,6 +4592,18 @@ filament_max_volumetric_speed = 13 inherits = Prusament PC Blend @PG; *PC08PG* filament_max_volumetric_speed = 18 +[filament:Prusament PC Blend @MK4] +inherits = Prusament PC Blend; *PCMK4* +filament_max_volumetric_speed = 9 + +[filament:Prusament PC Blend @MK4 0.6] +inherits = Prusament PC Blend @MK4; *PC06MK4* +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend @MK4 0.8] +inherits = Prusament PC Blend @MK4; *PC08MK4* +filament_max_volumetric_speed = 18 + [filament:Prusament PC Blend @MK2] inherits = Prusament PC Blend first_layer_bed_temperature = 105 @@ -3506,7 +4623,7 @@ fan_below_layer_time = 10 filament_colour = #BBBBBB filament_retract_length = nil filament_retract_lift = nil -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PC Blend Carbon Fiber @PG] inherits = Prusament PC Blend Carbon Fiber; *PCPG* @@ -3519,6 +4636,17 @@ filament_max_volumetric_speed = 13 inherits = Prusament PC Blend Carbon Fiber; *PC08PG* filament_max_volumetric_speed = 18 +[filament:Prusament PC Blend Carbon Fiber @MK4] +inherits = Prusament PC Blend Carbon Fiber; *PCMK4* + +[filament:Prusament PC Blend Carbon Fiber @MK4 0.6] +inherits = Prusament PC Blend Carbon Fiber; *PC06MK4* +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend Carbon Fiber @MK4 0.8] +inherits = Prusament PC Blend Carbon Fiber; *PC08MK4* +filament_max_volumetric_speed = 18 + [filament:Prusament PC Blend Carbon Fiber @MK2] inherits = Prusament PC Blend Carbon Fiber first_layer_bed_temperature = 105 @@ -3539,7 +4667,7 @@ temperature = 285 first_layer_bed_temperature = 90 bed_temperature = 115 fan_below_layer_time = 10 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PA11 Carbon Fiber @PG] inherits = Prusament PA11 Carbon Fiber; *PCPG* @@ -3553,6 +4681,18 @@ filament_max_volumetric_speed = 8 inherits = Prusament PA11 Carbon Fiber @PG; *PC08PG* filament_max_volumetric_speed = 10 +[filament:Prusament PA11 Carbon Fiber @MK4] +inherits = Prusament PA11 Carbon Fiber; *PCMK4* +filament_max_volumetric_speed = 6.5 + +[filament:Prusament PA11 Carbon Fiber @MK4 0.6] +inherits = Prusament PA11 Carbon Fiber @MK4; *PC06MK4* +filament_max_volumetric_speed = 8 + +[filament:Prusament PA11 Carbon Fiber @MK4 0.8] +inherits = Prusament PA11 Carbon Fiber @MK4; *PC08MK4* +filament_max_volumetric_speed = 10 + [filament:Prusament PA11 Carbon Fiber @MK2] inherits = Prusament PA11 Carbon Fiber first_layer_bed_temperature = 90 @@ -3646,7 +4786,7 @@ inherits = *ABSC* filament_vendor = Generic filament_cost = 27.82 filament_density = 1.04 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic ABS @PG] inherits = Generic ABS; *ABSPG* @@ -3660,6 +4800,17 @@ inherits = Generic ABS @PG; *ABS08PG* first_layer_temperature = 265 temperature = 265 +[filament:Generic ABS @MK4] +inherits = Generic ABS; *ABSMK4* + +[filament:Generic ABS @MK4 0.6] +inherits = Generic ABS @MK4; *ABS06MK4* + +[filament:Generic ABS @MK4 0.8] +inherits = Generic ABS @MK4; *ABS08MK4* +first_layer_temperature = 265 +temperature = 265 + [filament:Esun ABS] inherits = *ABSC* filament_vendor = Esun @@ -3677,6 +4828,15 @@ inherits = Esun ABS @PG; *ABS06PG* [filament:Esun ABS @PG 0.8] inherits = Esun ABS @PG; *ABS08PG* +[filament:Esun ABS @MK4] +inherits = Esun ABS; *ABSMK4* + +[filament:Esun ABS @MK4 0.6] +inherits = Esun ABS @MK4; *ABS06MK4* + +[filament:Esun ABS @MK4 0.8] +inherits = Esun ABS @MK4; *ABS08MK4* + [filament:Hatchbox ABS] inherits = *ABSC* filament_vendor = Hatchbox @@ -3694,6 +4854,15 @@ inherits = Hatchbox ABS @PG; *ABS06PG* [filament:Hatchbox ABS @PG 0.8] inherits = Hatchbox ABS @PG; *ABS08PG* +[filament:Hatchbox ABS @MK4] +inherits = Hatchbox ABS; *ABSMK4* + +[filament:Hatchbox ABS @MK4 0.6] +inherits = Hatchbox ABS @MK4; *ABS06MK4* + +[filament:Hatchbox ABS @MK4 0.8] +inherits = Hatchbox ABS @MK4; *ABS08MK4* + [filament:Filament PM ABS] inherits = *ABSC* renamed_from = "Plasty Mladec ABS" @@ -3712,6 +4881,15 @@ inherits = Filament PM ABS @PG; *ABS06PG* [filament:Filament PM ABS @PG 0.8] inherits = Filament PM ABS @PG; *ABS08PG* +[filament:Filament PM ABS @MK4] +inherits = Filament PM ABS; *ABSMK4* + +[filament:Filament PM ABS @MK4 0.6] +inherits = Filament PM ABS @MK4; *ABS06MK4* + +[filament:Filament PM ABS @MK4 0.8] +inherits = Filament PM ABS @MK4; *ABS08MK4* + [filament:Verbatim ABS] inherits = *ABSC* filament_vendor = Verbatim @@ -3730,13 +4908,22 @@ inherits = Verbatim ABS @PG; *ABS06PG* [filament:Verbatim ABS @PG 0.8] inherits = Verbatim ABS @PG; *ABS08PG* +[filament:Verbatim ABS @MK4] +inherits = Verbatim ABS; *ABSMK4* + +[filament:Verbatim ABS @MK4 0.6] +inherits = Verbatim ABS @MK4; *ABS06MK4* + +[filament:Verbatim ABS @MK4 0.8] +inherits = Verbatim ABS @MK4; *ABS08MK4* + [filament:Generic PETG] inherits = *PET* renamed_from = "Generic PET" filament_vendor = Generic filament_cost = 27.82 filament_density = 1.27 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic PETG @PG] inherits = Generic PETG; *PETPG* @@ -3766,12 +4953,15 @@ filament_spool_weight = 230 [filament:Extrudr DuraPro ASA @PG] inherits = Extrudr DuraPro ASA; *ABSPG* filament_max_volumetric_speed = 10 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr DuraPro ASA @PG 0.6] inherits = Extrudr DuraPro ASA @PG; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr DuraPro ASA @PG 0.8] inherits = Extrudr DuraPro ASA @PG; *ABS08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr PETG] inherits = *PET* @@ -3809,7 +4999,7 @@ filament_density = 1.29 filament_notes = "https://www.extrudr.com/en/products/catalogue/?material=198" first_layer_temperature = 235 temperature = 235 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) filament_spool_weight = 230 [filament:Extrudr XPETG CF @PG] @@ -4030,6 +5220,17 @@ inherits = Extrudr Flex Hard @PG; *FLEX06PG* [filament:Extrudr Flex Hard @PG 0.8] inherits = Extrudr Flex Hard @PG; *FLEX08PG* +[filament:Extrudr Flex Hard @MK4] +inherits = Extrudr Flex Hard; *FLEXMK4* +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 + +[filament:Extrudr Flex Hard @MK4 0.6] +inherits = Extrudr Flex Hard @MK4; *FLEX06MK4* + +[filament:Extrudr Flex Hard @MK4 0.8] +inherits = Extrudr Flex Hard @MK4; *FLEX08MK4* + [filament:Extrudr Flex Medium] inherits = *FLEX* filament_vendor = Extrudr @@ -4056,6 +5257,17 @@ inherits = Extrudr Flex Medium @PG; *FLEX06PG* [filament:Extrudr Flex Medium @PG 0.8] inherits = Extrudr Flex Medium @PG; *FLEX08PG* +[filament:Extrudr Flex Medium @MK4] +inherits = Extrudr Flex Medium; *FLEXMK4* +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 + +[filament:Extrudr Flex Medium @MK4 0.6] +inherits = Extrudr Flex Medium @MK4; *FLEX06MK4* + +[filament:Extrudr Flex Medium @MK4 0.8] +inherits = Extrudr Flex Medium @MK4; *FLEX08MK4* + [filament:Extrudr Flex SemiSoft] inherits = *FLEX* filament_vendor = Extrudr @@ -4085,6 +5297,20 @@ filament_max_volumetric_speed = 5 inherits = Extrudr Flex SemiSoft @PG; *FLEX08PG* filament_max_volumetric_speed = 8 +[filament:Extrudr Flex SemiSoft @MK4] +inherits = Extrudr Flex SemiSoft; *FLEXMK4* +extrusion_multiplier = 1.1 +filament_retract_length = 3 +filament_max_volumetric_speed = 3 + +[filament:Extrudr Flex SemiSoft @MK4 0.6] +inherits = Extrudr Flex SemiSoft @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 5 + +[filament:Extrudr Flex SemiSoft @MK4 0.8] +inherits = Extrudr Flex SemiSoft @MK4; *FLEX08MK4* +filament_max_volumetric_speed = 8 + [filament:addnorth Adamant S1] inherits = *FLEX* filament_vendor = addnorth @@ -4121,12 +5347,27 @@ filament_retract_lift = 0.2 [filament:addnorth Adamant S1 @PG 0.6] inherits = addnorth Adamant S1 @PG; *FLEX06PG* -filament_max_volumetric_speed = 5.5 +filament_max_volumetric_speed = 4.5 [filament:addnorth Adamant S1 @PG 0.8] inherits = addnorth Adamant S1 @PG; *FLEX08PG* filament_max_volumetric_speed = 9 +[filament:addnorth Adamant S1 @MK4] +inherits = addnorth Adamant S1; *FLEXMK4* +filament_max_volumetric_speed = 3 +filament_retract_length = 1.5 +filament_retract_restart_extra = 0 +filament_retract_lift = 0.2 + +[filament:addnorth Adamant S1 @MK4 0.6] +inherits = addnorth Adamant S1 @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 5.5 + +[filament:addnorth Adamant S1 @MK4 0.8] +inherits = addnorth Adamant S1 @MK4; *FLEX08MK4* +filament_max_volumetric_speed = 9 + [filament:addnorth Adura X] inherits = *PET* filament_vendor = addnorth @@ -4152,21 +5393,39 @@ filament_retract_lift = 0.4 filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" filament_spool_weight = 0 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and printer_model!="MK2SMM" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! single_extruder_multi_material [filament:addnorth Adura X @PG] inherits = addnorth Adura X; *PETPG* first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 4 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL" and ! single_extruder_multi_material [filament:addnorth Adura X @PG 0.6] inherits = addnorth Adura X @PG; *PET06PG* filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model=="XL" and ! single_extruder_multi_material [filament:addnorth Adura X @PG 0.8] inherits = addnorth Adura X @PG; *PET08PG* filament_max_volumetric_speed = 8 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=="XL" and ! single_extruder_multi_material + +[filament:addnorth Adura X @MK4] +inherits = addnorth Adura X; *PETPG* +filament_max_volumetric_speed = 4 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="MK4" and ! single_extruder_multi_material + +[filament:addnorth Adura X @MK4 0.6] +inherits = addnorth Adura X @MK4; *PET06PG* +filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model=="MK4" and ! single_extruder_multi_material + +[filament:addnorth Adura X @MK4 0.8] +inherits = addnorth Adura X @MK4; *PET08PG* +filament_max_volumetric_speed = 8 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=="MK4" and ! single_extruder_multi_material [filament:addnorth Adura X @MINI] inherits = addnorth Adura X @@ -4305,6 +5564,19 @@ filament_max_volumetric_speed = 6 inherits = addnorth OBC Polyethylene @PG; *FLEX08PG* filament_max_volumetric_speed = 10 +[filament:addnorth OBC Polyethylene @MK4] +inherits = addnorth OBC Polyethylene; *FLEXMK4* +filament_max_volumetric_speed = 4 +filament_retract_length = 1.5 + +[filament:addnorth OBC Polyethylene @MK4 0.6] +inherits = addnorth OBC Polyethylene @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6 + +[filament:addnorth OBC Polyethylene @MK4 0.8] +inherits = addnorth OBC Polyethylene @MK4; *FLEX08MK4* +filament_max_volumetric_speed = 10 + [filament:addnorth PETG] inherits = *PET* filament_vendor = addnorth @@ -4375,7 +5647,7 @@ filament_retract_length = 1.4 filament_max_volumetric_speed = 5 filament_spool_weight = 0 filament_notes = "Please use a nozzle that is resistant to abrasive filaments, like hardened steel." -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and printer_model!="MK2SMM" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! single_extruder_multi_material [filament:addnorth Rigid X @PG] inherits = addnorth Rigid X; *PETPG*; *04PLUSPG* @@ -4421,7 +5693,7 @@ slowdown_below_layer_time = 15 min_print_speed = 20 filament_spool_weight = 0 filament_retract_length = 1 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:addnorth Textura @PG] inherits = addnorth Textura; *PLAPG* @@ -4458,19 +5730,22 @@ disable_fan_first_layers = 3 fan_below_layer_time = 60 slowdown_below_layer_time = 15 bridge_fan_speed = 20 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG] inherits = Filamentworld ABS; *ABSPG* first_layer_bed_temperature = 100 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG 0.6] inherits = Filamentworld ABS @PG; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG 0.8] inherits = Filamentworld ABS @PG; *ABS08PG* first_layer_temperature = 240 temperature = 240 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @MINI] inherits = Filamentworld ABS @@ -4552,7 +5827,7 @@ filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.27 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Filament PM PETG @PG] inherits = Filament PM PETG; *PETPG* @@ -4568,7 +5843,7 @@ inherits = *PLA* filament_vendor = Generic filament_cost = 25.4 filament_density = 1.24 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic PLA @PG] inherits = Generic PLA; *PLAPG* @@ -4707,6 +5982,15 @@ inherits = 3D-Fuel Workday ABS @PG; *ABS06PG* [filament:3D-Fuel Workday ABS @PG 0.8] inherits = 3D-Fuel Workday ABS @PG; *ABS08PG* +[filament:3D-Fuel Workday ABS @MK4] +inherits = 3D-Fuel Workday ABS; *ABSMK4* + +[filament:3D-Fuel Workday ABS @MK4 0.6] +inherits = 3D-Fuel Workday ABS @MK4; *ABS06MK4* + +[filament:3D-Fuel Workday ABS @MK4 0.8] +inherits = 3D-Fuel Workday ABS @MK4; *ABS08MK4* + [filament:3D-Fuel Workday ABS @MINI] inherits = 3D-Fuel Workday ABS; *ABSMINI* @@ -4817,7 +6101,7 @@ filament_max_volumetric_speed = 1.2 filament_retract_length = 0 filament_retract_speed = nil filament_retract_lift = nil -compatible_printers_condition = nozzle_diameter[0]>=0.35 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.35 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic FLEX @PG] inherits = Generic FLEX; *FLEXPG* @@ -4835,10 +6119,32 @@ extrusion_multiplier = 1.08 [filament:Generic FLEX @PG 0.6] inherits = Generic FLEX @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 +filament_max_volumetric_speed = 4 [filament:Generic FLEX @PG 0.8] inherits = Generic FLEX @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Generic FLEX @MK4] +inherits = Generic FLEX; *FLEXMK4* +filament_max_volumetric_speed = 3 +filament_retract_length = 2.5 +fan_always_on = 1 +min_fan_speed = 30 +max_fan_speed = 30 +cooling = 1 +filament_retract_lift = 0 +slowdown_below_layer_time = 10 +first_layer_temperature = 230 +temperature = 230 +extrusion_multiplier = 1.08 + +[filament:Generic FLEX @MK4 0.6] +inherits = Generic FLEX @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6 + +[filament:Generic FLEX @MK4 0.8] +inherits = Generic FLEX @MK4; *FLEX08MK4* filament_max_volumetric_speed = 9 [filament:Fillamentum Flexfill 92A] @@ -4860,16 +6166,30 @@ full_fan_speed_layer = 6 [filament:Fillamentum Flexfill 92A @PG] inherits = Fillamentum Flexfill 92A; *FLEXPG* -filament_max_volumetric_speed = 3.5 +filament_max_volumetric_speed = 3 extrusion_multiplier = 1.1 filament_retract_length = 3.5 [filament:Fillamentum Flexfill 92A @PG 0.6] inherits = Fillamentum Flexfill 92A @PG; *FLEX06PG* -filament_max_volumetric_speed = 6.5 +filament_max_volumetric_speed = 4.5 [filament:Fillamentum Flexfill 92A @PG 0.8] inherits = Fillamentum Flexfill 92A @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Fillamentum Flexfill 92A @MK4] +inherits = Fillamentum Flexfill 92A; *FLEXMK4* +filament_max_volumetric_speed = 3.5 +extrusion_multiplier = 1.1 +filament_retract_length = 3.5 + +[filament:Fillamentum Flexfill 92A @MK4 0.6] +inherits = Fillamentum Flexfill 92A @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6.5 + +[filament:Fillamentum Flexfill 92A @MK4 0.8] +inherits = Fillamentum Flexfill 92A @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:AmazonBasics TPU] @@ -4906,10 +6226,23 @@ extrusion_multiplier = 1.1 [filament:AmazonBasics TPU @PG 0.6] inherits = AmazonBasics TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 6.5 +filament_max_volumetric_speed = 5 [filament:AmazonBasics TPU @PG 0.8] inherits = AmazonBasics TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:AmazonBasics TPU @MK4] +inherits = AmazonBasics TPU; *FLEXMK4* +filament_retract_length = 2.5 +extrusion_multiplier = 1.1 + +[filament:AmazonBasics TPU @MK4 0.6] +inherits = AmazonBasics TPU @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6.5 + +[filament:AmazonBasics TPU @MK4 0.8] +inherits = AmazonBasics TPU @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:SainSmart TPU] @@ -4948,10 +6281,25 @@ filament_retract_length = 1.5 [filament:SainSmart TPU @PG 0.6] inherits = SainSmart TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 6 [filament:SainSmart TPU @PG 0.8] inherits = SainSmart TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 9 + +[filament:SainSmart TPU @MK4] +inherits = SainSmart TPU; *FLEXMK4* +filament_max_volumetric_speed = 5 +first_layer_temperature = 235 +temperature = 235 +filament_retract_length = 1.5 + +[filament:SainSmart TPU @MK4 0.6] +inherits = SainSmart TPU @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 7 + +[filament:SainSmart TPU @MK4 0.8] +inherits = SainSmart TPU @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:NinjaTek NinjaFlex TPU] @@ -4983,16 +6331,30 @@ cooling = 1 [filament:NinjaTek NinjaFlex TPU @PG] inherits = NinjaTek NinjaFlex TPU; *FLEXPG* -filament_max_volumetric_speed = 3.5 +filament_max_volumetric_speed = 3 filament_retract_length = 3.5 extrusion_multiplier = 1.12 [filament:NinjaTek NinjaFlex TPU @PG 0.6] inherits = NinjaTek NinjaFlex TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 6.5 +filament_max_volumetric_speed = 4.5 [filament:NinjaTek NinjaFlex TPU @PG 0.8] inherits = NinjaTek NinjaFlex TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:NinjaTek NinjaFlex TPU @MK4] +inherits = NinjaTek NinjaFlex TPU; *FLEXMK4* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3.5 +extrusion_multiplier = 1.12 + +[filament:NinjaTek NinjaFlex TPU @MK4 0.6] +inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6.5 + +[filament:NinjaTek NinjaFlex TPU @MK4 0.8] +inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:NinjaTek Cheetah TPU] @@ -5008,15 +6370,28 @@ temperature = 240 [filament:NinjaTek Cheetah TPU @PG] inherits = NinjaTek Cheetah TPU; *FLEXPG* -filament_max_volumetric_speed = 6 +filament_max_volumetric_speed = 5 filament_retract_length = 2.2 [filament:NinjaTek Cheetah TPU @PG 0.6] inherits = NinjaTek Cheetah TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 8 +filament_max_volumetric_speed = 7 [filament:NinjaTek Cheetah TPU @PG 0.8] inherits = NinjaTek Cheetah TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 10 + +[filament:NinjaTek Cheetah TPU @MK4] +inherits = NinjaTek Cheetah TPU; *FLEXMK4* +filament_max_volumetric_speed = 6 +filament_retract_length = 2.2 + +[filament:NinjaTek Cheetah TPU @MK4 0.6] +inherits = NinjaTek Cheetah TPU @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 8 + +[filament:NinjaTek Cheetah TPU @MK4 0.8] +inherits = NinjaTek Cheetah TPU @MK4; *FLEX08MK4* filament_max_volumetric_speed = 12 [filament:NinjaTek Cheetah TPU @MINI] @@ -5070,6 +6445,19 @@ filament_max_volumetric_speed = 5 inherits = Filatech FilaFlex40 @PG; *FLEX08PG* filament_max_volumetric_speed = 10 +[filament:Filatech FilaFlex40 @MK4] +inherits = Filatech FilaFlex40; *FLEXMK4* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.5 + +[filament:Filatech FilaFlex40 @MK4 0.6] +inherits = Filatech FilaFlex40 @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 5 + +[filament:Filatech FilaFlex40 @MK4 0.8] +inherits = Filatech FilaFlex40 @MK4; *FLEX08MK4* +filament_max_volumetric_speed = 10 + [filament:Filatech FilaFlex30] inherits = Filatech FilaFlex40 temperature = 225 @@ -5084,10 +6472,23 @@ filament_retract_length = 3 [filament:Filatech FilaFlex30 @PG 0.6] inherits = Filatech FilaFlex30 @PG; *FLEX06PG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 5 [filament:Filatech FilaFlex30 @PG 0.8] inherits = Filatech FilaFlex30 @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Filatech FilaFlex30 @MK4] +inherits = Filatech FilaFlex30; *FLEXMK4* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3 + +[filament:Filatech FilaFlex30 @MK4 0.6] +inherits = Filatech FilaFlex30 @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 7 + +[filament:Filatech FilaFlex30 @MK4 0.8] +inherits = Filatech FilaFlex30 @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:Filatech FilaFlex55] @@ -5132,16 +6533,30 @@ temperature = 235 [filament:Filatech TPU @PG] inherits = Filatech TPU; *FLEXPG* -filament_max_volumetric_speed = 5.5 +filament_max_volumetric_speed = 4.5 first_layer_temperature = 235 filament_retract_length = 2.2 [filament:Filatech TPU @PG 0.6] inherits = Filatech TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 5 [filament:Filatech TPU @PG 0.8] inherits = Filatech TPU @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Filatech TPU @MK4] +inherits = Filatech TPU; *FLEXMK4* +filament_max_volumetric_speed = 5.5 +first_layer_temperature = 235 +filament_retract_length = 2.2 + +[filament:Filatech TPU @MK4 0.6] +inherits = Filatech TPU @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 7 + +[filament:Filatech TPU @MK4 0.8] +inherits = Filatech TPU @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:Filatech ABS] @@ -5161,6 +6576,15 @@ inherits = Filatech ABS @PG; *ABS06PG* [filament:Filatech ABS @PG 0.8] inherits = Filatech ABS @PG; *ABS08PG* +[filament:Filatech ABS @MK4] +inherits = Filatech ABS; *ABSMK4* + +[filament:Filatech ABS @MK4 0.6] +inherits = Filatech ABS @MK4; *ABS06MK4* + +[filament:Filatech ABS @MK4 0.8] +inherits = Filatech ABS @MK4; *ABS08MK4* + [filament:Filatech ABS @MINI] inherits = Filatech ABS; *ABSMINI* @@ -5172,17 +6596,20 @@ extrusion_multiplier = 0.95 filament_density = 1.1 first_layer_bed_temperature = 105 bed_temperature = 100 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG] inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG* first_layer_bed_temperature = 100 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG 0.6] inherits = Filatech FilaCarbon @PG; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG 0.8] inherits = Filatech FilaCarbon @PG; *ABS08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @MINI] inherits = Filatech FilaCarbon; *ABSMINI* @@ -5251,12 +6678,15 @@ cooling = 0 [filament:Filatech FilaTough @PG] inherits = Filatech FilaTough; *ABSPG* +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaTough @PG 0.6] inherits = Filatech FilaTough; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaTough @PG 0.8] inherits = Filatech FilaTough; *ABS08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech HIPS] inherits = Prusa HIPS @@ -5268,7 +6698,7 @@ first_layer_temperature = 230 first_layer_bed_temperature = 100 temperature = 225 bed_temperature = 110 -compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech HIPS @PG] inherits = Filatech HIPS; *ABSPG* @@ -5290,6 +6720,24 @@ inherits = Filatech HIPS @PG; *ABS06PG* [filament:Filatech HIPS @PG 0.8] inherits = Filatech HIPS @PG; *ABS08PG* +[filament:Filatech HIPS @MK4] +inherits = Filatech HIPS; *ABSMK4* +bridge_fan_speed = 50 +cooling = 1 +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 10 +first_layer_temperature = 220 +temperature = 225 +max_fan_speed = 20 +min_fan_speed = 20 + +[filament:Filatech HIPS @MK4 0.6] +inherits = Filatech HIPS @MK4; *ABS06MK4* + +[filament:Filatech HIPS @MK4 0.8] +inherits = Filatech HIPS @MK4; *ABS08MK4* + [filament:Filatech HIPS @MINI] inherits = Filatech HIPS; *ABSMINI* @@ -5307,7 +6755,7 @@ cooling = 0 bridge_fan_speed = 25 filament_type = PA filament_max_volumetric_speed = 8 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech PA @PG] inherits = Filatech PA; *ABSPG* @@ -5323,6 +6771,18 @@ filament_max_volumetric_speed = 10 inherits = Filatech PA @PG; *ABS08PG* filament_max_volumetric_speed = 12 +[filament:Filatech PA @MK4] +inherits = Filatech PA; *ABSMK4* +filament_max_volumetric_speed = 8 + +[filament:Filatech PA @MK4 0.6] +inherits = Filatech PA @MK4; *ABS06MK4* +filament_max_volumetric_speed = 10 + +[filament:Filatech PA @MK4 0.8] +inherits = Filatech PA @MK4; *ABS08MK4* +filament_max_volumetric_speed = 12 + [filament:Filatech PA @MK2] inherits = Filatech PA first_layer_bed_temperature = 105 @@ -5351,6 +6811,15 @@ inherits = Filatech PC @PG; *PC06PG* [filament:Filatech PC @PG 0.8] inherits = Filatech PC @PG; *PC08PG* +[filament:Filatech PC @MK4] +inherits = Filatech PC; *PCMK4* + +[filament:Filatech PC @MK4 0.6] +inherits = Filatech PC @MK4; *PC06MK4* + +[filament:Filatech PC @MK4 0.8] +inherits = Filatech PC @MK4; *PC08MK4* + [filament:Filatech PC @MK2] inherits = Filatech PC first_layer_bed_temperature = 105 @@ -5379,6 +6848,15 @@ inherits = Filatech PC-ABS; *PC06PG* [filament:Filatech PC-ABS @PG 0.8] inherits = Filatech PC-ABS; *PC08PG* +[filament:Filatech PC-ABS @MK4] +inherits = Filatech PC-ABS; *PCMK4* + +[filament:Filatech PC-ABS @MK4 0.6] +inherits = Filatech PC-ABS @MK4; *PC06MK4* + +[filament:Filatech PC-ABS @MK4 0.8] +inherits = Filatech PC-ABS @MK4; *PC08MK4* + [filament:Filatech PC-ABS @MK2] inherits = Filatech PC-ABS first_layer_bed_temperature = 105 @@ -5474,7 +6952,7 @@ filament_retract_lift = 0 filament_retract_speed = 40 filament_retract_before_travel = 2 filament_retract_layer_change = 0 -compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PRO1 @PG] inherits = Ultrafuse PRO1; *PLAPG* @@ -5519,6 +6997,15 @@ inherits = Ultrafuse ABS @PG; *ABS06PG* [filament:Ultrafuse ABS @PG 0.8] inherits = Ultrafuse ABS @PG; *ABS08PG* +[filament:Ultrafuse ABS @MK4] +inherits = Ultrafuse ABS; *ABSMK4* + +[filament:Ultrafuse ABS @MK4 0.6] +inherits = Ultrafuse ABS @MK4; *ABS06MK4* + +[filament:Ultrafuse ABS @MK4 0.8] +inherits = Ultrafuse ABS @MK4; *ABS08MK4* + [filament:Ultrafuse ABS @MINI] inherits = Ultrafuse ABS; *ABSMINI* filament_retract_layer_change = nil @@ -5547,6 +7034,15 @@ inherits = Ultrafuse ABS Fusion+ @PG; *ABS06PG* [filament:Ultrafuse ABS Fusion+ @PG 0.8] inherits = Ultrafuse ABS Fusion+ @PG; *ABS08PG* +[filament:Ultrafuse ABS Fusion+ @MK4] +inherits = Ultrafuse ABS Fusion+; *ABSMK4* + +[filament:Ultrafuse ABS Fusion+ @MK4 0.6] +inherits = Ultrafuse ABS Fusion+ @MK4; *ABS06MK4* + +[filament:Ultrafuse ABS Fusion+ @MK4 0.8] +inherits = Ultrafuse ABS Fusion+ @MK4; *ABS08MK4* + [filament:Ultrafuse ABS Fusion+ @MINI] inherits = Ultrafuse ABS Fusion+; *ABSMINI* first_layer_bed_temperature = 100 @@ -5582,6 +7078,18 @@ filament_max_volumetric_speed = 9 inherits = Ultrafuse ASA @PG; *ABS08PG* filament_max_volumetric_speed = 12 +[filament:Ultrafuse ASA @MK4] +inherits = Ultrafuse ASA; *ABSMK4* +filament_max_volumetric_speed = 5 + +[filament:Ultrafuse ASA @MK4 0.6] +inherits = Ultrafuse ASA @MK4; *ABS06MK4* +filament_max_volumetric_speed = 9 + +[filament:Ultrafuse ASA @MK4 0.8] +inherits = Ultrafuse ASA @MK4; *ABS08MK4* +filament_max_volumetric_speed = 12 + [filament:Ultrafuse ASA @MINI] inherits = Ultrafuse ASA; *ABSMINI* filament_type = ASA @@ -5607,6 +7115,15 @@ inherits = Ultrafuse HIPS @PG; *ABS06PG* [filament:Ultrafuse HIPS @PG 0.8] inherits = Ultrafuse HIPS @PG; *ABS08PG* +[filament:Ultrafuse HIPS @MK4] +inherits = Ultrafuse HIPS; *ABSMK4* + +[filament:Ultrafuse HIPS @MK4 0.6] +inherits = Ultrafuse HIPS @MK4; *ABS06MK4* + +[filament:Ultrafuse HIPS @MK4 0.8] +inherits = Ultrafuse HIPS @MK4; *ABS08MK4* + [filament:Ultrafuse HIPS @MINI] inherits = Ultrafuse HIPS; *ABSMINI* filament_type = HIPS @@ -5635,21 +7152,24 @@ filament_retract_before_travel = 2 filament_retract_layer_change = 0 filament_cost = 0 filament_spool_weight = 0 -compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" filament_notes = "Material Description\nThe key features of Ultrafuse PA are the high strength and high modulus. Furthermore, Ultrafuse PA shows a good thermal distortion stability.\n\nPrinting Recommendations:\nApply PVA glue, Kapton tape or PA adhesive to a clean buildplate to improve adhesion." [filament:Ultrafuse PA @PG] inherits = Ultrafuse PA; *ABSPG* filament_max_volumetric_speed = 8 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA @PG 0.6] inherits = Ultrafuse PA @PG; *ABS06PG* filament_max_volumetric_speed = 10 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA @PG 0.8] inherits = Ultrafuse PA @PG; *ABS08PG* filament_max_volumetric_speed = 12 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA6 GF30] inherits = Ultrafuse PA @@ -5673,17 +7193,19 @@ filament_deretract_speed = 30 filament_retract_lift = nil filament_wipe = 0 filament_notes = "Material Description\nUltrafuse® PA6 GF30 is a unique compound specifically developed for FFF printing. Due to the glass fiber content of 30%, parts tend to warp less. In addition the excellent layer adhesion and its compatibility with the water soluble support Ultrafuse® BVOH make this material the perfect solution to develop industrial applications on an FFF printer.\n\nWith its high wear and chemical resistance, high stiffness and strength, Ultrafuse® PA6 GF30 is perfect for a wide variety of applications in automotive, electronics or transportation.\n\nUltrafuse PA6 GF30 is designed for functional prototyping and demanding applications such as industrial tooling, transportation, electronics, small appliances, sports & leisure\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PA6 GF30 can be printed directly onto a clean build plate. For challenging prints, use Magigoo PA gluestick to improve adhesion." -compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA6 GF30 @PG 0.6] inherits = Ultrafuse PA6 GF30; *ABS06PG* filament_max_volumetric_speed = 10 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA6 GF30 @PG 0.8] inherits = Ultrafuse PA6 GF30; *ABS08PG* filament_max_volumetric_speed = 14 first_layer_temperature = 275 temperature = 275 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PAHT-CF15] inherits = Ultrafuse PA6 GF30 @@ -5692,9 +7214,11 @@ filament_notes = "Material Description\nPAHT CF15 is a high-performance 3D print [filament:Ultrafuse PAHT-CF15 @PG 0.6] inherits = Ultrafuse PAHT-CF15; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PAHT-CF15 @PG 0.8] inherits = Ultrafuse PAHT-CF15; *ABS08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PC-ABS-FR] inherits = Ultrafuse ABS @@ -5711,7 +7235,7 @@ min_fan_speed = 20 max_fan_speed = 20 bridge_fan_speed = 30 disable_fan_first_layers = 4 -compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS – two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion." start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" @@ -5729,6 +7253,18 @@ filament_max_volumetric_speed = 10 inherits = Ultrafuse PC-ABS-FR @PG; *ABS08PG* filament_max_volumetric_speed = 12 +[filament:Ultrafuse PC-ABS-FR @MK4] +inherits = Ultrafuse PC-ABS-FR; *ABSMK4* +filament_max_volumetric_speed = 8 + +[filament:Ultrafuse PC-ABS-FR @MK4 0.6] +inherits = Ultrafuse PC-ABS-FR @MK4; *ABS06MK4* +filament_max_volumetric_speed = 10 + +[filament:Ultrafuse PC-ABS-FR @MK4 0.8] +inherits = Ultrafuse PC-ABS-FR @MK4; *ABS08MK4* +filament_max_volumetric_speed = 12 + [filament:Ultrafuse PET-CF15] inherits = Ultrafuse PET filament_density = 1.36 @@ -5752,7 +7288,7 @@ filament_retract_lift = nil filament_wipe = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" filament_notes = "Material Description\nPET CF15 is a Carbon Fiber reinforced PET which has precisely tuned material properties, for a wide range of technical applications. The filament is very strong and stiff and has high heat resistance. With its high dimensional stability and low abrasiveness, the filament offers an easy to print experience which allows direct printing on glass or a PEI sheet. It is compatible with HiPS for breakaway support and water soluble support and has an excellent surface finish.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nUltrafuse PET-CF15 can be printed directly onto a clean build plate. For challenging prints, use 3dLac to improve adhesion." -compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PET-CF15 @PG 0.6] inherits = Ultrafuse PET; *PET06PG* @@ -5803,19 +7339,22 @@ filament_deretract_speed = 25 filament_retract_layer_change = 0 filament_wipe = nil filament_notes = "Material Description\nUltrafuse PP is high-performance thermoplastic with low density, high elasticity and high resistance to fatigue. The mechanical properties make it an ideal material for 3D-printing applications which have to endure high stress or strain. The filament has high chemical resistance and a high isolation value. PP is one of the most used materials in the world, due to its versatility and ability to engineer lightweight tough parts.\n\nPrinting Recommendations:\nApply PP tape or Magigoo PP adhesive to the buildplate for optimal adhesion." -compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG] inherits = Ultrafuse PP; *ABSPG* filament_max_volumetric_speed = 2.5 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG 0.6] inherits = Ultrafuse PP @PG; *ABS06PG* filament_max_volumetric_speed = 4 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG 0.8] inherits = Ultrafuse PP @PG; *ABS08PG* filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP-GF30] inherits = Ultrafuse PP @@ -5837,13 +7376,15 @@ filament_deretract_speed = 30 filament_retract_lift = nil filament_wipe = 0 filament_notes = "Ultrafuse PP GF30 is polypropylene, reinforced with 30% glass fiber content. The fibers in this compound are specially designed for 3D-printing filaments and are compatible with a wide range of standard FFF 3D-printers. The extreme stiffness makes this material highly suitable for demanding applications. Other key properties of PPGF30 are high heat resistance and improved UV-resistance. All these excellent properties make this filament highly suitable in an industrial environment.\n\nPrinting Recommendations:\nThis material contains fibers that have an abrasive effect on printer components. Use a hardened nozzle with a diameter of 0.6 or larger for optimal performance and avoid damage to the nozzle.\n\nApply PP strapping tape or PPGF adhesive to a clean build plate for optimal adhesion." -compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI" and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP-GF30 @PG 0.6] inherits = Ultrafuse PP; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP-GF30 @PG 0.8] inherits = Ultrafuse PP; *ABS08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse TPC-45D] inherits = *FLEX* @@ -5934,10 +7475,26 @@ filament_retract_length = 3.5 [filament:Ultrafuse TPU-85A @PG 0.6] inherits = Ultrafuse TPU-85A @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 +filament_max_volumetric_speed = 4 [filament:Ultrafuse TPU-85A @PG 0.8] inherits = Ultrafuse TPU-85A @PG; *FLEX08PG* +filament_max_volumetric_speed = 7 + +[filament:Ultrafuse TPU-85A @MK4] +inherits = Ultrafuse TPU-85A; *FLEXMK4* +filament_max_volumetric_speed = 3 +extrusion_multiplier = 1.1 +first_layer_temperature = 220 +temperature = 215 +filament_retract_length = 3.5 + +[filament:Ultrafuse TPU-85A @MK4 0.6] +inherits = Ultrafuse TPU-85A @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6 + +[filament:Ultrafuse TPU-85A @MK4 0.8] +inherits = Ultrafuse TPU-85A @MK4; *FLEX08MK4* filament_max_volumetric_speed = 8 [filament:Ultrafuse TPU-95A] @@ -5954,10 +7511,23 @@ filament_retract_length = 3 [filament:Ultrafuse TPU-95A @PG 0.6] inherits = Ultrafuse TPU-95A @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 +filament_max_volumetric_speed = 3.5 [filament:Ultrafuse TPU-95A @PG 0.8] inherits = Ultrafuse TPU-95A @PG; *FLEX08PG* +filament_max_volumetric_speed = 6 + +[filament:Ultrafuse TPU-95A @MK4] +inherits = Ultrafuse TPU-95A; *FLEXMK4* +filament_max_volumetric_speed = 2.5 +filament_retract_length = 3 + +[filament:Ultrafuse TPU-95A @MK4 0.6] +inherits = Ultrafuse TPU-95A @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 5 + +[filament:Ultrafuse TPU-95A @MK4 0.8] +inherits = Ultrafuse TPU-95A @MK4; *FLEX08MK4* filament_max_volumetric_speed = 7 [filament:Ultrafuse rPET] @@ -6008,7 +7578,7 @@ cooling = 0 fan_always_on = 0 filament_max_volumetric_speed = 4 filament_type = METAL -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material start_filament_gcode = "M900 K0" filament_colour = #FFFFFF @@ -6016,11 +7586,13 @@ filament_colour = #FFFFFF inherits = Ultrafuse Metal; *ABSPG*; *04PLUSPG* filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K0" +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse Metal @PG 0.6] inherits = Ultrafuse Metal @PG; *ABS06PG* filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K0" +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Polymaker PC-Max] inherits = *ABS* @@ -6050,6 +7622,18 @@ filament_max_volumetric_speed = 12 inherits = Polymaker PC-Max @PG; *ABS08PG* filament_max_volumetric_speed = 15 +[filament:Polymaker PC-Max @MK4] +inherits = Polymaker PC-Max; *ABSMK4* +filament_max_volumetric_speed = 8 + +[filament:Polymaker PC-Max @MK4 0.6] +inherits = Polymaker PC-Max @MK4; *ABS06MK4* +filament_max_volumetric_speed = 12 + +[filament:Polymaker PC-Max @MK4 0.8] +inherits = Polymaker PC-Max @MK4; *ABS08MK4* +filament_max_volumetric_speed = 15 + [filament:PrimaSelect PVA+] inherits = *PLA* filament_vendor = PrimaSelect @@ -6086,7 +7670,7 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusa ABS @PG] inherits = Prusa ABS; *ABSPG* @@ -6100,6 +7684,17 @@ inherits = Prusa ABS @PG; *ABS08PG* first_layer_temperature = 265 temperature = 265 +[filament:Prusa ABS @MK4] +inherits = Prusa ABS; *ABSMK4* + +[filament:Prusa ABS @MK4 0.6] +inherits = Prusa ABS @MK4; *ABS06MK4* + +[filament:Prusa ABS @MK4 0.8] +inherits = Prusa ABS @MK4; *ABS08MK4* +first_layer_temperature = 265 +temperature = 265 + [filament:*ABS MMU2*] inherits = Prusa ABS compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.25 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material @@ -6330,7 +7925,7 @@ max_fan_speed = 20 min_fan_speed = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" temperature = 220 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic HIPS] inherits = *ABS* @@ -6350,7 +7945,7 @@ max_fan_speed = 20 min_fan_speed = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" temperature = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic HIPS @PG] inherits = Generic HIPS; *ABSPG* @@ -6362,6 +7957,15 @@ inherits = Generic HIPS @PG; *ABS06PG* [filament:Generic HIPS @PG 0.8] inherits = Generic HIPS @PG; *ABS08PG* +[filament:Generic HIPS @MK4] +inherits = Generic HIPS; *ABSMK4* + +[filament:Generic HIPS @MK4 0.6] +inherits = Generic HIPS @MK4; *ABS06MK4* + +[filament:Generic HIPS @MK4 0.8] +inherits = Generic HIPS @MK4; *ABS08MK4* + [filament:Prusa PETG] inherits = *PET* renamed_from = "Prusa PET" @@ -6369,7 +7973,7 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.27 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusa PETG @PG] inherits = Prusa PETG; *PETPG* @@ -6386,7 +7990,7 @@ filament_vendor = Verbatim filament_cost = 27.90 filament_density = 1.27 filament_spool_weight = 235 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PETG @PG] inherits = Verbatim PETG; *PETPG* @@ -6406,7 +8010,7 @@ filament_cost = 36.29 filament_density = 1.27 filament_spool_weight = 201 filament_type = PETG -compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PETG @PG] inherits = Prusament PETG; *PETPG* @@ -6428,7 +8032,7 @@ extrusion_multiplier = 1.03 filament_cost = 54.99 filament_density = 1.27 filament_colour = #BBBBBB -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:Prusament PETG Carbon Fiber @PG] inherits = Prusament PETG; *PETPG*; *04PLUSPG* @@ -6439,34 +8043,34 @@ inherits = Prusament PETG Carbon Fiber @PG; *PET06PG* [filament:Prusament PETG Carbon Fiber @PG 0.8] inherits = Prusament PETG Carbon Fiber @PG; *PET08PG* -[filament:Prusament PETG Tungsten 75%] -inherits = *PET* -filament_vendor = Prusa Polymers -filament_colour = #BBBBBB -first_layer_temperature = 255 -temperature = 265 -filament_cost = 277.09 -filament_density = 4 -filament_spool_weight = 201 -filament_type = PETG -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -cooling = 0 -min_fan_speed = 50 -max_fan_speed = 70 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.01{else}0{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif nozzle_diameter[0]==0.4}M900 K12{else}M900 K0{endif} ; Filament gcode LA 1.0" +## [filament:Prusament PETG Tungsten 75%] +## inherits = *PET* +## filament_vendor = Prusa Polymers +## filament_colour = #BBBBBB +## first_layer_temperature = 255 +## temperature = 265 +## filament_cost = 277.09 +## filament_density = 4 +## filament_spool_weight = 201 +## filament_type = PETG +## compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +## cooling = 0 +## min_fan_speed = 50 +## max_fan_speed = 70 +## start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.01{else}0{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif nozzle_diameter[0]==0.4}M900 K12{else}M900 K0{endif} ; Filament gcode LA 1.0" -[filament:Prusament PETG Tungsten 75% @PG] -inherits = Prusament PETG Tungsten 75%; *PETPG* -filament_max_volumetric_speed = 8 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.01{else}0{endif} ; Filament gcode" +## [filament:Prusament PETG Tungsten 75% @PG] +## inherits = Prusament PETG Tungsten 75%; *PETPG* +## filament_max_volumetric_speed = 8 +## start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.01{else}0{endif} ; Filament gcode" -[filament:Prusament PETG Tungsten 75% @PG 0.6] -inherits = Prusament PETG Tungsten 75% @PG; *PET06PG* -filament_max_volumetric_speed = 9 +## [filament:Prusament PETG Tungsten 75% @PG 0.6] +## inherits = Prusament PETG Tungsten 75% @PG; *PET06PG* +## filament_max_volumetric_speed = 9 -[filament:Prusament PETG Tungsten 75% @PG 0.8] -inherits = Prusament PETG Tungsten 75% @PG; *PET08PG* -filament_max_volumetric_speed = 10 +## [filament:Prusament PETG Tungsten 75% @PG 0.8] +## inherits = Prusament PETG Tungsten 75% @PG; *PET08PG* +## filament_max_volumetric_speed = 10 [filament:Prusa PETG @0.6 nozzle] inherits = *PET06* @@ -6635,7 +8239,7 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_density = 1.24 filament_spool_weight = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusa PLA @PG] inherits = Prusa PLA; *PLAPG* @@ -6781,6 +8385,17 @@ inherits = Eolas Prints TPU 93A @PG; *FLEX06PG* [filament:Eolas Prints TPU 93A @PG 0.8] inherits = Eolas Prints TPU 93A @PG; *FLEX08PG* +[filament:Eolas Prints TPU 93A @MK4] +inherits = Eolas Prints TPU 93A; *FLEXMK4* +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 + +[filament:Eolas Prints TPU 93A @MK4 0.6] +inherits = Eolas Prints TPU 93A @MK4; *FLEX06MK4* + +[filament:Eolas Prints TPU 93A @MK4 0.8] +inherits = Eolas Prints TPU 93A @MK4; *FLEX08MK4* + [filament:Print With Smile PLA] inherits = *PLA* filament_vendor = Print With Smile @@ -6860,6 +8475,17 @@ inherits = Print With Smile ASA @PG; *ABS08PG* first_layer_temperature = 255 temperature = 255 +[filament:Print With Smile ASA @MK4] +inherits = Print With Smile ASA; *ABSMK4* + +[filament:Print With Smile ASA @MK4 0.6] +inherits = Print With Smile ASA @MK4; *ABS06MK4* + +[filament:Print With Smile ASA @MK4 0.8] +inherits = Print With Smile ASA @MK4; *ABS08MK4* +first_layer_temperature = 255 +temperature = 255 + [filament:Print With Smile ABS] inherits = *ABSC* filament_vendor = Print With Smile @@ -6882,6 +8508,15 @@ inherits = Print With Smile ABS @PG; *ABS06PG* [filament:Print With Smile ABS @PG 0.8] inherits = Print With Smile ABS @PG; *ABS08PG* +[filament:Print With Smile ABS @MK4] +inherits = Print With Smile ABS; *ABSMK4* + +[filament:Print With Smile ABS @MK4 0.6] +inherits = Print With Smile ABS @MK4; *ABS06MK4* + +[filament:Print With Smile ABS @MK4 0.8] +inherits = Print With Smile ABS @MK4; *ABS08MK4* + [filament:Print With Smile PETG CF] inherits = Extrudr XPETG CF filament_vendor = Print With Smile @@ -6930,14 +8565,27 @@ filament_deretract_speed = 20 [filament:Print With Smile TPU96A @PG] inherits = Print With Smile TPU96A; *FLEXPG* filament_retract_length = 2 -filament_max_volumetric_speed = 3 +filament_max_volumetric_speed = 2.5 [filament:Print With Smile TPU96A @PG 0.6] inherits = Print With Smile TPU96A @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 +filament_max_volumetric_speed = 3.5 [filament:Print With Smile TPU96A @PG 0.8] inherits = Print With Smile TPU96A @PG; *FLEX08PG* +filament_max_volumetric_speed = 7 + +[filament:Print With Smile TPU96A @MK4] +inherits = Print With Smile TPU96A; *FLEXMK4* +filament_retract_length = 2 +filament_max_volumetric_speed = 3 + +[filament:Print With Smile TPU96A @MK4 0.6] +inherits = Print With Smile TPU96A @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 5 + +[filament:Print With Smile TPU96A @MK4 0.8] +inherits = Print With Smile TPU96A @MK4; *FLEX08MK4* filament_max_volumetric_speed = 8 [filament:Fiberlogy Easy PLA] @@ -6966,7 +8614,7 @@ filament_vendor = Fiberlogy filament_spool_weight = 330 filament_cost = 20 filament_density = 1.27 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material first_layer_bed_temperature = 80 bed_temperature = 80 first_layer_temperature = 235 @@ -7019,6 +8667,15 @@ inherits = Fiberlogy ASA @PG; *ABS06PG* [filament:Fiberlogy ASA @PG 0.8] inherits = Fiberlogy ASA @PG; *ABS08PG* +[filament:Fiberlogy ASA @MK4] +inherits = Fiberlogy ASA; *ABSMK4* + +[filament:Fiberlogy ASA @MK4 0.6] +inherits = Fiberlogy ASA @MK4; *ABS06MK4* + +[filament:Fiberlogy ASA @MK4 0.8] +inherits = Fiberlogy ASA @MK4; *ABS08MK4* + [filament:Fiberlogy ASA @MINI] inherits = Fiberlogy ASA; *ABSMINI* @@ -7042,12 +8699,15 @@ disable_fan_first_layers = 5 [filament:Fiberlogy Easy ABS @PG] inherits = Fiberlogy Easy ABS; *ABSPG* +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @PG 0.6] inherits = Fiberlogy Easy ABS; *ABS06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @PG 0.8] inherits = Fiberlogy Easy ABS; *ABS08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @MINI] inherits = Fiberlogy Easy ABS; *ABSMINI* @@ -7139,7 +8799,7 @@ filament_spool_weight = 330 [filament:Fiberlogy FiberFlex 40D @PG] inherits = Fiberlogy FiberFlex 40D; *FLEXPG* -filament_max_volumetric_speed = 4 +filament_max_volumetric_speed = 3 filament_retract_length = 2.2 extrusion_multiplier = 1.1 first_layer_temperature = 220 @@ -7147,10 +8807,26 @@ temperature = 220 [filament:Fiberlogy FiberFlex 40D @PG 0.6] inherits = Fiberlogy FiberFlex 40D @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 +filament_max_volumetric_speed = 4.5 [filament:Fiberlogy FiberFlex 40D @PG 0.8] inherits = Fiberlogy FiberFlex 40D @PG; *FLEX08PG* +filament_max_volumetric_speed = 9 + +[filament:Fiberlogy FiberFlex 40D @MK4] +inherits = Fiberlogy FiberFlex 40D; *FLEXMK4* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.2 +extrusion_multiplier = 1.1 +first_layer_temperature = 220 +temperature = 220 + +[filament:Fiberlogy FiberFlex 40D @MK4 0.6] +inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6 + +[filament:Fiberlogy FiberFlex 40D @MK4 0.8] +inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberFlex 40D @MINI] @@ -7184,15 +8860,28 @@ filament_retract_length = 1.2 [filament:Fiberlogy MattFlex 40D @PG] inherits = Fiberlogy MattFlex 40D; *FLEXPG* -filament_max_volumetric_speed = 4 +filament_max_volumetric_speed = 3 filament_retract_length = 2.2 [filament:Fiberlogy MattFlex 40D @PG 0.6] inherits = Fiberlogy MattFlex 40D @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 +filament_max_volumetric_speed = 4.5 [filament:Fiberlogy MattFlex 40D @PG 0.8] inherits = Fiberlogy MattFlex 40D @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Fiberlogy MattFlex 40D @MK4] +inherits = Fiberlogy MattFlex 40D; *FLEXMK4* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.2 + +[filament:Fiberlogy MattFlex 40D @MK4 0.6] +inherits = Fiberlogy MattFlex 40D @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 6 + +[filament:Fiberlogy MattFlex 40D @MK4 0.8] +inherits = Fiberlogy MattFlex 40D @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberFlex 30D] @@ -7208,7 +8897,7 @@ filament_retract_length = 1.2 [filament:Fiberlogy FiberFlex 30D @PG] inherits = Fiberlogy FiberFlex 30D; *FLEXPG* -filament_max_volumetric_speed = 3.5 +filament_max_volumetric_speed = 2.5 filament_retract_length = 3 first_layer_temperature = 220 temperature = 220 @@ -7218,10 +8907,28 @@ extrusion_multiplier = 1.1 [filament:Fiberlogy FiberFlex 30D @PG 0.6] inherits = Fiberlogy FiberFlex 30D @PG; *FLEX06PG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 5 [filament:Fiberlogy FiberFlex 30D @PG 0.8] inherits = Fiberlogy FiberFlex 30D @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Fiberlogy FiberFlex 30D @MK4] +inherits = Fiberlogy FiberFlex 30D; *FLEXMK4* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3 +first_layer_temperature = 220 +temperature = 220 +first_layer_bed_temperature = 55 +bed_temperature = 55 +extrusion_multiplier = 1.1 + +[filament:Fiberlogy FiberFlex 30D @MK4 0.6] +inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 7 + +[filament:Fiberlogy FiberFlex 30D @MK4 0.8] +inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberSatin] @@ -7367,6 +9074,18 @@ filament_max_volumetric_speed = 8 inherits = Fiberlogy Nylon PA12 @PG; *ABS08PG* filament_max_volumetric_speed = 11 +[filament:Fiberlogy Nylon PA12 @MK4] +inherits = Fiberlogy Nylon PA12; *ABSMK4* +filament_max_volumetric_speed = 6 + +[filament:Fiberlogy Nylon PA12 @MK4 0.6] +inherits = Fiberlogy Nylon PA12 @MK4; *ABS06MK4* +filament_max_volumetric_speed = 8 + +[filament:Fiberlogy Nylon PA12 @MK4 0.8] +inherits = Fiberlogy Nylon PA12 @MK4; *ABS08MK4* +filament_max_volumetric_speed = 11 + [filament:Fiberlogy Nylon PA12+CF15] inherits = Fiberlogy Nylon PA12 extrusion_multiplier = 0.97 @@ -7382,13 +9101,14 @@ fan_below_layer_time = 20 bridge_fan_speed = 30 fan_always_on = 0 filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Nylon PA12+CF15 @PG] inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *04PLUSPG* first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL" and ! single_extruder_multi_material [filament:Fiberlogy Nylon PA12+CF15 @PG 0.6] inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS06PG* @@ -7398,6 +9118,19 @@ filament_max_volumetric_speed = 10 inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS08PG* filament_max_volumetric_speed = 12 +[filament:Fiberlogy Nylon PA12+CF15 @MK4] +inherits = Fiberlogy Nylon PA12+CF15; *ABSMK4*; *04PLUSPG* +filament_max_volumetric_speed = 8 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="MK4" and ! single_extruder_multi_material + +[filament:Fiberlogy Nylon PA12+CF15 @MK4 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS06MK4* +filament_max_volumetric_speed = 10 + +[filament:Fiberlogy Nylon PA12+CF15 @MK4 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS08MK4* +filament_max_volumetric_speed = 12 + [filament:Fiberlogy Nylon PA12+GF15] inherits = Fiberlogy Nylon PA12+CF15 filament_density = 1.13 @@ -7415,6 +9148,18 @@ filament_density = 1.13 inherits = Fiberlogy Nylon PA12+CF15 @PG 0.8 filament_density = 1.13 +[filament:Fiberlogy Nylon PA12+GF15 @MK4] +inherits = Fiberlogy Nylon PA12+CF15 @MK4 +filament_density = 1.13 + +[filament:Fiberlogy Nylon PA12+GF15 @MK4 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @MK4 0.6 +filament_density = 1.13 + +[filament:Fiberlogy Nylon PA12+GF15 @MK4 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @MK4 0.8 +filament_density = 1.13 + [filament:Fiberlogy PP] inherits = *ABS* filament_vendor = Fiberlogy @@ -7441,16 +9186,19 @@ filament_max_volumetric_speed = 5 [filament:Fiberlogy PP @PG] inherits = Fiberlogy PP; *ABSPG* filament_max_volumetric_speed = 5 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy PP @PG 0.6] inherits = Fiberlogy PP @PG; *ABS06PG* filament_max_volumetric_speed = 7 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy PP @PG 0.8] inherits = Fiberlogy PP @PG; *ABS08PG* filament_max_volumetric_speed = 10 first_layer_temperature = 250 temperature = 250 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filament PM PLA] inherits = *PLA* @@ -7602,7 +9350,7 @@ filament_cost = 36.29 filament_density = 1.24 filament_spool_weight = 201 filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa" -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PLA @PG] inherits = Prusament PLA; *PLAPG* @@ -7629,7 +9377,7 @@ filament_max_volumetric_speed = 8 filament_type = PVB filament_soluble = 1 filament_colour = #FFFF6F -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material slowdown_below_layer_time = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" @@ -7743,16 +9491,30 @@ filament_deretract_speed = 20 [filament:Fillamentum Flexfill 98A @PG] inherits = Fillamentum Flexfill 98A; *FLEXPG* -filament_max_volumetric_speed = 3 +filament_max_volumetric_speed = 2.5 filament_retract_length = 3 extrusion_multiplier = 1.08 [filament:Fillamentum Flexfill 98A @PG 0.6] inherits = Fillamentum Flexfill 98A @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 +filament_max_volumetric_speed = 3 [filament:Fillamentum Flexfill 98A @PG 0.8] inherits = Fillamentum Flexfill 98A @PG; *FLEX08PG* +filament_max_volumetric_speed = 8 + +[filament:Fillamentum Flexfill 98A @MK4] +inherits = Fillamentum Flexfill 98A; *FLEXMK4* +filament_max_volumetric_speed = 3 +filament_retract_length = 3 +extrusion_multiplier = 1.08 + +[filament:Fillamentum Flexfill 98A @MK4 0.6] +inherits = Fillamentum Flexfill 98A @MK4; *FLEX06MK4* +filament_max_volumetric_speed = 4 + +[filament:Fillamentum Flexfill 98A @MK4 0.8] +inherits = Fillamentum Flexfill 98A @MK4; *FLEX08MK4* filament_max_volumetric_speed = 10 [filament:ColorFabb VarioShore TPU] @@ -7770,6 +9532,10 @@ temperature = 220 inherits = ColorFabb VarioShore TPU; *FLEXPG* filament_max_volumetric_speed = 1.5 +[filament:ColorFabb VarioShore TPU @MK4] +inherits = ColorFabb VarioShore TPU; *FLEXMK4* +filament_max_volumetric_speed = 1.5 + [filament:Taulman Bridge] inherits = *common* filament_vendor = Taulman @@ -7791,7 +9557,7 @@ temperature = 260 max_fan_speed = 0 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman Bridge @PG] inherits = Taulman Bridge; *ABSPG* @@ -7808,6 +9574,20 @@ filament_max_volumetric_speed = 12 first_layer_temperature = 270 temperature = 270 +[filament:Taulman Bridge @MK4] +inherits = Taulman Bridge; *ABSMK4* +filament_max_volumetric_speed = 7 + +[filament:Taulman Bridge @MK4 0.6] +inherits = Taulman Bridge @MK4; *ABS06MK4* +filament_max_volumetric_speed = 9 + +[filament:Taulman Bridge @MK4 0.8] +inherits = Taulman Bridge @MK4; *ABS08MK4* +filament_max_volumetric_speed = 12 +first_layer_temperature = 270 +temperature = 270 + [filament:Taulman Bridge @MINI] inherits = Taulman Bridge bed_temperature = 90 @@ -7844,14 +9624,17 @@ temperature = 250 [filament:Fillamentum Nylon FX256 @PG] inherits = Fillamentum Nylon FX256; *PAPG* filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fillamentum Nylon FX256 @PG 0.6] inherits = Fillamentum Nylon FX256 @PG; *PA06PG* filament_max_volumetric_speed = 8 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fillamentum Nylon FX256 @PG 0.8] inherits = Fillamentum Nylon FX256 @PG; *PA08PG* filament_max_volumetric_speed = 11 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro] inherits = *common* @@ -7881,19 +9664,28 @@ filament_retract_speed = 40 filament_retract_lift = nil filament_retract_before_travel = 1.5 filament_wipe = 0 -compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG] inherits = Fiberthree F3 PA Pure Pro; *PAPG* filament_max_volumetric_speed = 5 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG 0.6] inherits = Fiberthree F3 PA Pure Pro @PG; *PA06PG* filament_max_volumetric_speed = 7 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG 0.8] inherits = Fiberthree F3 PA Pure Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro] inherits = *common* @@ -7923,19 +9715,28 @@ filament_retract_speed = 40 filament_retract_lift = nil filament_retract_before_travel = 1.5 filament_wipe = 0 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG] inherits = Fiberthree F3 PA-CF Pro; *PAPG* filament_max_volumetric_speed = 5 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG 0.6] inherits = Fiberthree F3 PA-CF Pro; *PA06PG* filament_max_volumetric_speed = 7 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG 0.8] inherits = Fiberthree F3 PA-CF Pro; *PA08PG* filament_max_volumetric_speed = 10 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro] inherits = Fiberthree F3 PA-CF Pro @@ -7949,14 +9750,23 @@ min_fan_speed = 15 [filament:Fiberthree F3 PA-GF Pro @PG] inherits = Fiberthree F3 PA-GF Pro; *PAPG* filament_max_volumetric_speed = 5 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro @PG 0.6] inherits = Fiberthree F3 PA-GF Pro @PG; *PA06PG* filament_max_volumetric_speed = 7 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro @PG 0.8] inherits = Fiberthree F3 PA-GF Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro] inherits = Prusament PC Blend Carbon Fiber @@ -7969,7 +9779,7 @@ temperature = 285 first_layer_bed_temperature = 90 bed_temperature = 90 fan_below_layer_time = 10 -compatible_printers_condition = printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material max_fan_speed = 15 min_fan_speed = 15 filament_type = PA @@ -7978,14 +9788,23 @@ filament_max_volumetric_speed = 6 [filament:Fiberthree F3 PA-GF30 Pro @PG] inherits = Fiberthree F3 PA-GF30 Pro; *PAPG* filament_max_volumetric_speed = 6 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro @PG 0.6] inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA06PG* filament_max_volumetric_speed = 7.5 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro @PG 0.8] inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 +bed_temperature = 90 +first_layer_bed_temperature = 90 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase] inherits = *PET* @@ -8003,12 +9822,15 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no [filament:Taulman T-Glase @PG] inherits = Taulman T-Glase; *PAPG* +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase @PG 0.6] inherits = Taulman T-Glase @PG; *PA06PG* +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase @PG 0.8] inherits = Taulman T-Glase @PG; *PA08PG* +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PLA] inherits = *PLA* @@ -8055,14 +9877,17 @@ temperature = 210 [filament:Verbatim BVOH @PG] inherits = Verbatim BVOH; *ABSPG* filament_max_volumetric_speed = 4 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @PG 0.6] inherits = Verbatim BVOH @PG; *ABS06PG* filament_max_volumetric_speed = 6 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @PG 0.8] inherits = Verbatim BVOH @PG; *ABS08PG* filament_max_volumetric_speed = 9 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @MMU2] inherits = Verbatim BVOH @@ -8148,14 +9973,17 @@ temperature = 220 [filament:Verbatim PP @PG] inherits = Verbatim PP; *ABSPG* filament_max_volumetric_speed = 5 +compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PP @PG 0.6] inherits = Verbatim PP @PG; *ABS06PG* filament_max_volumetric_speed = 7 +compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PP @PG 0.8] inherits = Verbatim PP @PG; *ABS08PG* filament_max_volumetric_speed = 10 +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:FormFutura Centaur PP] inherits = *common* @@ -8181,7 +10009,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 235 filament_wipe = 0 filament_retract_lift = 0 -compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:FormFutura Centaur PP @PG] inherits = FormFutura Centaur PP; *PETPG* @@ -8437,12 +10265,12 @@ filament_cost = 54.99 filament_colour = #BBBBBB compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 -[filament:Prusament PETG Tungsten 75% @MINI] -inherits = Prusament PETG Tungsten 75%; *PETMINI* -full_fan_speed_layer = 5 -start_filament_gcode = "M900 K0" -filament_colour = #BBBBBB -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" +## [filament:Prusament PETG Tungsten 75% @MINI] +## inherits = Prusament PETG Tungsten 75%; *PETMINI* +## full_fan_speed_layer = 5 +## start_filament_gcode = "M900 K0" +## filament_colour = #BBBBBB +## compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Kimya PETG Carbon @MINI] inherits = Kimya PETG Carbon; *PETMINI* @@ -8697,14 +10525,14 @@ first_layer_temperature = 220 temperature = 220 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic ABS @0.8 nozzle] inherits = Generic ABS first_layer_temperature = 265 temperature = 265 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic PETG @0.8 nozzle] inherits = Generic PETG @@ -8713,7 +10541,7 @@ temperature = 250 filament_max_volumetric_speed = 20 filament_retract_lift = 0.2 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusa PLA @0.8 nozzle] inherits = Prusa PLA @@ -8721,7 +10549,7 @@ first_layer_temperature = 220 temperature = 220 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusa PETG @0.8 nozzle] inherits = Prusa PETG @@ -8729,26 +10557,26 @@ first_layer_temperature = 240 temperature = 250 filament_max_volumetric_speed = 20 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusa ABS @0.8 nozzle] inherits = Prusa ABS first_layer_temperature = 265 temperature = 265 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic FLEX @0.8 nozzle] inherits = Generic FLEX filament_max_volumetric_speed = 4.3 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Generic HIPS @0.8 nozzle] inherits = Generic HIPS first_layer_temperature = 240 temperature = 240 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PLA @0.8 nozzle] inherits = Prusament PLA @@ -8756,7 +10584,7 @@ first_layer_temperature = 225 temperature = 225 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PETG @0.8 nozzle] inherits = Prusament PETG @@ -8765,7 +10593,7 @@ temperature = 260 filament_max_volumetric_speed = 20 filament_retract_lift = 0.2 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PETG Carbon Fiber @0.8 nozzle] inherits = Prusament PETG @0.8 nozzle @@ -8781,64 +10609,64 @@ first_layer_temperature = 265 temperature = 265 filament_max_volumetric_speed = 15 slowdown_below_layer_time = 20 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PC Blend @0.8 nozzle] inherits = Prusament PC Blend filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PC Blend Carbon Fiber @0.8 nozzle] inherits = Prusament PC Blend Carbon Fiber filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PA11 Carbon Fiber @0.8 nozzle] inherits = Prusament PA11 Carbon Fiber filament_max_volumetric_speed = 11 -compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]==0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material [filament:Prusament PA11 Carbon Fiber @0.8 nozzle MK2] inherits = Prusament PA11 Carbon Fiber @MK2 filament_max_volumetric_speed = 11 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="XL" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) [filament:Prusament PC Blend @0.8 nozzle MK2] inherits = Prusament PC Blend @MK2 filament_max_volumetric_speed = 13 filament_retract_lift = 0.25 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="XL" and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and single_extruder_multi_material) [filament:Prusament PVB @0.8 nozzle] inherits = Prusament PVB first_layer_temperature = 225 temperature = 225 filament_max_volumetric_speed = 15 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and ! single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material slowdown_below_layer_time = 20 ## Filaments 0.8 nozzle MMU2 [filament:Generic HIPS @MMU2 0.8 nozzle] inherits = Generic HIPS @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material [filament:Prusament ASA @MMU2 0.8 nozzle] inherits = Prusament ASA @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material slowdown_below_layer_time = 20 filament_max_volumetric_speed = 14 [filament:Prusament PC Blend @MMU2 0.8 nozzle] inherits = Prusament PC Blend @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 12 [filament:Generic PETG @MMU2 0.8 nozzle] inherits = Generic PETG @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 18 first_layer_temperature = 240 temperature = 240 @@ -8847,7 +10675,7 @@ filament_ramming_parameters = "120 140 5.51613 5.6129 5.70968 5.77419 5.77419 5. [filament:Prusament PETG @MMU2 0.8 nozzle] inherits = Prusament PETG @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 18 first_layer_temperature = 240 temperature = 240 @@ -8864,26 +10692,26 @@ filament_colour = #BBBBBB [filament:Generic PLA @MMU2 0.8 nozzle] inherits = Generic PLA @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 14 first_layer_temperature = 215 temperature = 210 [filament:Prusament PLA @MMU2 0.8 nozzle] inherits = Prusament PLA @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 14 first_layer_temperature = 215 temperature = 210 [filament:Verbatim BVOH @MMU2 0.8 nozzle] inherits = Verbatim BVOH @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 8 [filament:PrimaSelect PVA+ @MMU2 0.8 nozzle] inherits = PrimaSelect PVA+ @MMU2 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="XL" and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material +compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes!~/.*PG.*/ and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material filament_max_volumetric_speed = 8 ## Filaments 0.8 nozzle MINI @@ -9413,6 +11241,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #EC0000 +[sla_material:Prusament Resin Model Solid Grey @0.025] +inherits = *common 0.025* +exposure_time = 4.5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #9C9D9D + +[sla_material:Prusament Resin Model Alabaster White @0.025] +inherits = *common 0.025* +exposure_time = 4.5 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #D6D7D8 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.025] inherits = *common 0.025* exposure_time = 7 @@ -10426,6 +12270,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #EC0000 +[sla_material:Prusament Resin Model Solid Grey @0.05] +inherits = *common 0.05* +exposure_time = 5 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #9C9D9D + +[sla_material:Prusament Resin Model Alabaster White @0.05] +inherits = *common 0.05* +exposure_time = 5 +initial_exposure_time = 30 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #D6D7D8 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.05] inherits = *common 0.05* exposure_time = 8 @@ -10878,6 +12738,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #3AD200 +[sla_material:Prusament Resin Model Solid Grey @0.1] +inherits = *common 0.1* +exposure_time = 13 +initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #9C9D9D + +[sla_material:Prusament Resin Model Alabaster White @0.1] +inherits = *common 0.1* +exposure_time = 13 +initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #D6D7D8 + [sla_material:Prusament Resin BioBased60 Magma Red @0.1] inherits = *common 0.1* exposure_time = 13 @@ -11134,6 +13010,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #EC0000 +[sla_material:Prusament Resin Model Solid Grey @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #9C9D9D + +[sla_material:Prusament Resin Model Alabaster White @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 2 +initial_exposure_time = 20 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #D6D7D8 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.025 SL1S] inherits = *0.025_sl1s* exposure_time = 3.5 @@ -11612,6 +13504,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #EC0000 +[sla_material:Prusament Resin Model Solid Grey @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2.2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #9C9D9D + +[sla_material:Prusament Resin Model Alabaster White @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2.2 +initial_exposure_time = 20 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #D6D7D8 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.05 SL1S] inherits = *0.05_sl1s* exposure_time = 4 @@ -12294,6 +14202,30 @@ material_type = Tough material_vendor = 3DJake material_colour = #007EFD +[sla_material:LOCTITE 3D IND475 @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 4 +initial_exposure_time = 15 +material_type = Flexible +material_vendor = Henkel +material_colour = #FFFFFF + +[sla_material:LOCTITE 3D PRO476 @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 7 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Henkel +material_colour = #595959 + +[sla_material:LOCTITE 3D 3843 HDT60 High Toughness @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 13 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Henkel +material_colour = #595959 + ## 0.1 SL1S ## Prusa Polymers 0.1 @@ -12394,6 +14326,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #EC0000 +[sla_material:Prusament Resin Model Solid Grey @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #9C9D9D + +[sla_material:Prusament Resin Model Alabaster White @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.8 +initial_exposure_time = 20 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #D6D7D8 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.1 SL1S] inherits = *0.1_sl1s* exposure_time = 5 @@ -12754,6 +14702,30 @@ material_type = Tough material_vendor = 3DJake material_colour = #007EFD +[sla_material:LOCTITE 3D IND475 @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 6.5 +initial_exposure_time = 15 +material_type = Flexible +material_vendor = Henkel +material_colour = #FFFFFF + +[sla_material:LOCTITE 3D PRO476 @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 9 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Henkel +material_colour = #595959 + +[sla_material:LOCTITE 3D 3843 HDT60 High Toughness @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 18 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Henkel +material_colour = #595959 + [printer:*common*] printer_technology = FFF bed_shape = 0x0,250x0,250x210,0x210 @@ -13577,7 +15549,7 @@ max_layer_height = 0.25 min_layer_height = 0.07 silent_mode = 0 remaining_times = 1 -printer_notes = PRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_XL\nPG +printer_notes = Do not remove the keywords below.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_XL\nPG retract_lift_below = 359 retract_speed = 35 deretract_speed = 25 @@ -13643,13 +15615,120 @@ machine_max_acceleration_travel = 1500 inherits = *commonXL* printer_variant = 0.8 nozzle_diameter = 0.8 -retract_length = 0.8 +retract_length = 0.6 +retract_before_wipe = 50% retract_lift = 0.25 +retract_speed = 25 +deretract_speed = 15 max_layer_height = 0.6 min_layer_height = 0.2 default_print_profile = 0.40mm QUALITY @XL 0.8 default_filament_profile = "Prusament PLA @PG 0.8" +[printer:*commonMK4*] +inherits = *common* +bed_shape = 0x0,250x0,250x210,0x210 +max_print_height = 220 +printer_variant = 0.4 +printer_model = MK4 +nozzle_diameter = 0.4 +end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM900 K0 ; reset LA\nM142 S36 ; reset heatbreak target temp\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] +machine_limits_usage = emit_to_gcode +machine_max_acceleration_e = 2500 +machine_max_acceleration_extruding = 2000 +machine_max_acceleration_retracting = 1200 +machine_max_acceleration_travel = 2000 +machine_max_acceleration_x = 2500 +machine_max_acceleration_y = 2500 +machine_max_acceleration_z = 200 +machine_max_feedrate_e = 100 +machine_max_feedrate_x = 200 +machine_max_feedrate_y = 200 +machine_max_feedrate_z = 40 +machine_max_jerk_e = 10 +machine_max_jerk_x = 8 +machine_max_jerk_y = 8 +machine_max_jerk_z = 2 +machine_min_extruding_rate = 0 +machine_min_travel_rate = 0 +max_layer_height = 0.25 +min_layer_height = 0.07 +silent_mode = 0 +remaining_times = 1 +printer_notes = Do not remove the keywords below.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK4\nPG +retract_lift_below = 219 +retract_speed = 35 +deretract_speed = 25 +retract_before_travel = 1.5 +retract_before_wipe = 80% +retract_layer_change = 1 +retract_length = 0.8 +start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="NYLON"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\n; probe to clean the nozzle\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)+32} Y{((first_layer_print_min[1]) - 4)} Z{5} F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{32} H{4}\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} Z{40} F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 ; mesh bed leveling\nM104 S[first_layer_temperature] ; set extruder temp\nG0 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4) + 4 - 4.5} Z{30} F4800\n\nM109 S[first_layer_temperature] ; wait for extruder temp\nG1 Z0.2 F720\nG92 E0\n\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\n{if filament_type[initial_tool]=="FLEX"}\nG1 E4 F2400 ; deretraction\n{else}\nG1 E2 F2400 ; deretraction\n{endif}\n\n; move right\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32) + 32} E{32 * 0.15} F1000\n; move down\nG1 Y{(max(0, first_layer_print_min[1]) - 4) + 4 - 4.5 - 1.5} E{1.5 * 0.15} F1000\n; move left\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} E{32 * 0.30} F800\n\nG92 E0\nM221 S100 ; set flow to 100% +default_print_profile = 0.20mm QUALITY @MK4 0.4 +default_filament_profile = "Prusament PLA @PG" +thumbnails = 16x16,313x173,440x240 +thumbnails_format = PNG +gcode_flavor = marlin2 +high_current_on_filament_swap = 0 +retract_lift = 0.2 + +[printer:Original Prusa MK4 0.4 nozzle] +inherits = *commonMK4* +max_layer_height = 0.30 + +[printer:Original Prusa MK4 0.6 nozzle] +inherits = *commonMK4* +printer_variant = 0.6 +nozzle_diameter = 0.6 +retract_length = 0.7 +retract_lift = 0.2 +max_layer_height = 0.40 +min_layer_height = 0.15 +default_print_profile = 0.25mm QUALITY @MK4 0.6 + +[printer:Original Prusa MK4 0.5 nozzle] +inherits = *commonMK4* +printer_variant = 0.5 +nozzle_diameter = 0.5 +retract_length = 0.7 +max_layer_height = 0.32 +min_layer_height = 0.07 +default_print_profile = 0.20mm QUALITY @MK4 0.5 + +[printer:Original Prusa MK4 0.3 nozzle] +inherits = *commonMK4* +printer_variant = 0.3 +nozzle_diameter = 0.3 +retract_length = 0.7 +max_layer_height = 0.22 +min_layer_height = 0.05 +default_print_profile = 0.16mm QUALITY @MK4 0.3 + +[printer:Original Prusa MK4 0.25 nozzle] +inherits = *commonMK4* +printer_variant = 0.25 +nozzle_diameter = 0.25 +retract_length = 0.8 +retract_lift = 0.15 +max_layer_height = 0.15 +min_layer_height = 0.05 +default_print_profile = 0.12mm QUALITY @MK4 0.25 +machine_max_acceleration_travel = 1500 + +[printer:Original Prusa MK4 0.8 nozzle] +inherits = *commonMK4* +printer_variant = 0.8 +nozzle_diameter = 0.8 +retract_length = 0.6 +retract_before_wipe = 50% +retract_lift = 0.25 +retract_speed = 25 +deretract_speed = 15 +max_layer_height = 0.6 +min_layer_height = 0.2 +default_print_profile = 0.40mm QUALITY @MK4 0.8 +default_filament_profile = "Prusament PLA @PG 0.8" + [printer:Original Prusa SL1] printer_technology = SLA printer_model = SL1 From dcf5889cfaff31d3a849e5084965c41565cb5f53 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 29 Mar 2023 12:53:09 +0200 Subject: [PATCH 197/201] Fixed a save to 3mf: Added a check if cut information have to be saved to 3mf --- src/libslic3r/Format/3mf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 5a608cb05..a0d5e1360 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2958,6 +2958,8 @@ namespace Slic3r { unsigned int object_cnt = 0; for (const ModelObject* object : model.objects) { + if (!object->is_cut()) + continue; object_cnt++; pt::ptree& obj_tree = tree.add("objects.object", ""); From eb2c121f9475a04042e497bbb24c633bc32df831 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 29 Mar 2023 14:20:31 +0200 Subject: [PATCH 198/201] Follow-up https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/cc5660ad8cf28a134cad5164061d890441655612 - Fixed tooltip texts --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index d1623e41e..bab667676 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3000,8 +3000,8 @@ void GLGizmoEmboss::draw_advanced() } if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", (m_keep_up? - _u8L("Unlock the text's up orientation when moving text along the object's surface."): - _u8L("Lock the text's up orientation when moving text along the object's surface.") + _u8L("Unlock the text's rotation when moving text along the object's surface."): + _u8L("Lock the text's rotation when moving text along the object's surface.") ).c_str()); // when more collection add selector From 9ed97abdb0fe0e168d2a5b6909c34443243e4763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 29 Mar 2023 14:08:28 +0200 Subject: [PATCH 199/201] Fix of #10034 (SPE-1608): Fixed crash inside Arachne caused by degenerated Voronoi diagram. Boost Voronoi diagram produced degenerated Voronoi diagram. This Voronoi diagram was detected by our method for detection of those types of degeneration. But rotation by PI / 6 wasn't able to fix it. So now, when the Voronoi diagram is still degenerated after the first rotation, then we try another rotation by different angles to fix the degenerated Voronoi diagram. --- .../Arachne/SkeletalTrapezoidation.cpp | 112 ++++++++++-------- tests/libslic3r/test_arachne.cpp | 54 +++++++++ 2 files changed, 116 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index cd58f34c6..381f18b70 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -522,44 +522,6 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph) return false; } -inline static std::unordered_map try_to_fix_degenerated_voronoi_diagram_by_rotation( - Geometry::VoronoiDiagram &voronoi_diagram, - const Polygons &polys, - Polygons &polys_rotated, - std::vector &segments, - const double fix_angle) -{ - std::unordered_map vertex_mapping; - for (Polygon &poly : polys_rotated) - poly.rotate(fix_angle); - - assert(polys_rotated.size() == polys.size()); - for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) { - assert(polys_rotated[poly_idx].size() == polys[poly_idx].size()); - for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx) - vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]}); - } - - segments.clear(); - for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++) - for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++) - segments.emplace_back(&polys_rotated, poly_idx, point_idx); - - voronoi_diagram.clear(); - construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); - -#ifdef ARACHNE_DEBUG_VORONOI - { - static int iRun = 0; - dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys)); - } -#endif - - assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); - - return vertex_mapping; -} - inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, const double fix_angle, const std::unordered_map &vertex_mapping) @@ -626,6 +588,56 @@ VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::Voronoi return VoronoiDiagramStatus::NO_ISSUE_DETECTED; } +inline static std::pair, double> try_to_fix_degenerated_voronoi_diagram_by_rotation( + Geometry::VoronoiDiagram &voronoi_diagram, + const Polygons &polys, + Polygons &polys_rotated, + std::vector &segments, + const std::vector &fix_angles) +{ + const Polygons polys_rotated_original = polys_rotated; + double fixed_by_angle = fix_angles.front(); + std::unordered_map vertex_mapping; + + for (const double &fix_angle : fix_angles) { + vertex_mapping.clear(); + polys_rotated = polys_rotated_original; + fixed_by_angle = fix_angle; + + for (Polygon &poly : polys_rotated) + poly.rotate(fix_angle); + + assert(polys_rotated.size() == polys.size()); + for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) { + assert(polys_rotated[poly_idx].size() == polys[poly_idx].size()); + for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx) + vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]}); + } + + segments.clear(); + for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++) + for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++) + segments.emplace_back(&polys_rotated, poly_idx, point_idx); + + voronoi_diagram.clear(); + construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); + +#ifdef ARACHNE_DEBUG_VORONOI + { + static int iRun = 0; + dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys)); + } +#endif + + if (detect_voronoi_diagram_known_issues(voronoi_diagram, segments) == VoronoiDiagramStatus::NO_ISSUE_DETECTED) + break; + } + + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); + + return {vertex_mapping, fixed_by_angle}; +} + void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) { #ifdef ARACHNE_DEBUG @@ -669,8 +681,9 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) // When any Voronoi vertex is missing, the Voronoi diagram is not planar, or some voronoi edge is // intersecting input segment, rotate the input polygon and try again. - VoronoiDiagramStatus status = detect_voronoi_diagram_known_issues(voronoi_diagram, segments); - const double fix_angle = PI / 6; + VoronoiDiagramStatus status = detect_voronoi_diagram_known_issues(voronoi_diagram, segments); + const std::vector fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11}; + double fixed_by_angle = fix_angles.front(); std::unordered_map vertex_mapping; // polys_copy is referenced through items stored in the std::vector segments. @@ -683,16 +696,15 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) else if (status == VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT) BOOST_LOG_TRIVIAL(warning) << "Detected Voronoi edge intersecting input segment, input polygons will be rotated back and forth."; - vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle); + std::tie(vertex_mapping, fixed_by_angle) = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angles); - assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); - assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments)); - assert(!detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments)); - if (detect_missing_voronoi_vertex(voronoi_diagram, segments)) + VoronoiDiagramStatus status_after_fix = detect_voronoi_diagram_known_issues(voronoi_diagram, segments); + assert(status_after_fix == VoronoiDiagramStatus::NO_ISSUE_DETECTED); + if (status_after_fix == VoronoiDiagramStatus::MISSING_VORONOI_VERTEX) BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input."; - else if (!Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments)) + else if (status_after_fix == VoronoiDiagramStatus::NON_PLANAR_VORONOI_DIAGRAM) BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input."; - else if (detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments)) + else if (status_after_fix == VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT) BOOST_LOG_TRIVIAL(error) << "Detected Voronoi edge intersecting input segment even after the rotation of input."; } @@ -759,8 +771,8 @@ process_voronoi_diagram: // diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram. if (status == VoronoiDiagramStatus::NO_ISSUE_DETECTED && has_missing_twin_edge(this->graph)) { BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth."; - status = VoronoiDiagramStatus::OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION; - vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle); + status = VoronoiDiagramStatus::OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION; + std::tie(vertex_mapping, fixed_by_angle) = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angles); assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); if (detect_missing_voronoi_vertex(voronoi_diagram, segments)) @@ -784,7 +796,7 @@ process_voronoi_diagram: } if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) - rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fix_angle, vertex_mapping); + rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fixed_by_angle, vertex_mapping); #ifdef ARACHNE_DEBUG assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); diff --git a/tests/libslic3r/test_arachne.cpp b/tests/libslic3r/test_arachne.cpp index c4564f77c..61792c37f 100644 --- a/tests/libslic3r/test_arachne.cpp +++ b/tests/libslic3r/test_arachne.cpp @@ -689,4 +689,58 @@ TEST_CASE("Arachne - #8846 - Degenerated Voronoi diagram - Voronoi edges interse // Total extrusion length should be around 500mm when the part is ok and 680mm when it has perimeters in places where they shouldn't be. REQUIRE(total_extrusion_length <= scaled(500.)); +} + +// This test case was distilled from GitHub issue #10034. +// In this test case previous rotation by PI / 6 wasn't able to fix non-planar Voronoi diagram. +TEST_CASE("Arachne - #10034 - Degenerated Voronoi diagram - That wasn't fixed by rotation by PI / 6", "[ArachneDegeneratedDiagram10034RotationNotWorks]") { + Polygon poly_0 = { + Point(43612632, -25179766), Point(58456010, 529710), Point(51074898, 17305660), Point(49390982, 21042355), + Point(48102357, 23840161), Point(46769686, 26629546), Point(45835761, 28472742), Point(45205450, 29623133), + Point(45107431, 29878059), Point(45069846, 30174950), Point(45069846, 50759533), Point(-45069846, 50759533), + Point(-45069852, 29630557), Point(-45105780, 29339980), Point(-45179725, 29130704), Point(-46443313, 26398986), + Point(-52272109, 13471493), Point(-58205450, 95724), Point(-29075091, -50359531), Point(29075086, -50359531), + }; + + Polygon poly_1 = { + Point(-37733905, 45070445), Point(-37813254, 45116257), Point(-39353851, 47784650), Point(-39353851, 47876274), + Point(-38632470, 49125743), Point(-38553121, 49171555), Point(-33833475, 49171555), Point(-33754126, 49125743), + Point(-33032747, 47876277), Point(-33032747, 47784653), Point(-34007855, 46095721), Point(-34573350, 45116257), + Point(-34652699, 45070445), + }; + + Polygon poly_2 = { + Point(-44016799, 40706401), Point(-44116953, 40806555), Point(-44116953, 46126289), Point(-44016799, 46226443), + Point(-42211438, 46226443), Point(-42132089, 46180631), Point(-40591492, 43512233), Point(-40591492, 43420609), + Point(-41800123, 41327194), Point(-42132089, 40752213), Point(-42211438, 40706401), + }; + + Polygon poly_3 = { + Point(6218189, 10966609), Point(6138840, 11012421), Point(4598238, 13680817), Point(4598238, 13772441), Point(6138840, 16440843), + Point(6218189, 16486655), Point(9299389, 16486655), Point(9378738, 16440843), Point(10919340, 13772441), Point(10919340, 13680817), + Point(10149039, 12346618), Point(9378738, 11012421), Point(9299389, 10966609), + }; + + Polygon poly_4 = { + Point(13576879, 6718065), Point(13497530, 6763877), Point(11956926, 9432278), Point(11956926, 9523902), + Point(13497528, 12192302), Point(13576877, 12238114), Point(16658079, 12238112), Point(16737428, 12192300), + Point(18278031, 9523904), Point(18278031, 9432280), Point(17507729, 8098077), Point(16737428, 6763877), + Point(16658079, 6718065), + }; + + Polygons polygons = { + poly_0, poly_1, poly_2, poly_3, poly_4, + }; + + coord_t ext_perimeter_spacing = 407079; + coord_t perimeter_spacing = 407079; + coord_t inset_count = 1; + + Arachne::WallToolPaths wall_tool_paths(polygons, ext_perimeter_spacing, perimeter_spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults()); + wall_tool_paths.generate(); + std::vector perimeters = wall_tool_paths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-degenerated-diagram-10034-rotation-not-works.svg"), polygons, perimeters, union_ex(wall_tool_paths.getInnerContour())); +#endif } \ No newline at end of file From ab1813bdc84753b88f671653215c498c5c7e0c40 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 29 Mar 2023 15:32:33 +0200 Subject: [PATCH 200/201] PlaceholderParser: fixed access to "position" variable Follow-up to 59552a8aee893316a74b9a6e012d164ff7643b44 --- src/libslic3r/GCode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 22c26b3a5..ab6829111 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -467,6 +467,7 @@ void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) // Reserve buffer for current position. this->position.assign(3, 0); this->opt_position = new ConfigOptionFloats(this->position); + this->output_config.set_key_value("position", this->opt_position); // Store zhop variable into the parser itself, it is a read-only variable to the script. this->opt_zhop = new ConfigOptionFloat(writer.get_zhop()); this->parser.set("zhop", this->opt_zhop); From 7a5639f322e2aa03e5d7cb461cebc71e6325dcb0 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 29 Mar 2023 17:53:23 +0200 Subject: [PATCH 201/201] Fixed crash if toolchange G-code modifies final position, because the extruder was not set yet. --- src/libslic3r/GCode.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ab6829111..4156fe4ee 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3385,10 +3385,12 @@ Vec2d GCode::point_to_gcode_quantized(const Point &point) const // convert a model-space scaled point into G-code coordinates Point GCode::gcode_to_point(const Vec2d &point) const { - Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); - return Point( - scale_(point(0) - m_origin(0) + extruder_offset(0)), - scale_(point(1) - m_origin(1) + extruder_offset(1))); + Vec2d pt = point - m_origin; + if (const Extruder *extruder = m_writer.extruder(); extruder) + // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet. + pt += m_config.extruder_offset.get_at(extruder->id()); + return scaled(pt); + } } // namespace Slic3r