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/104] 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 cebef8b42b..663aafc773 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/104] 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 663aafc773..32dc65e9b0 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/104] 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 32dc65e9b0..4bc5d22edf 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 2ed6c20003..443d83d3be 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/104] 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 4bc5d22edf..8f7c3de017 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/104] 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 8f7c3de017..5d8cf47bf4 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/104] 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 5d8cf47bf4..1c8e5a28c6 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 443d83d3be..50bec2a287 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/104] 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 1c8e5a28c6..41ee661099 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 50bec2a287..77b86b8b2f 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/104] 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 41ee661099..2876d438c5 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/104] 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 2876d438c5..95736e271a 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 77b86b8b2f..0cfe34508e 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/104] 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 95736e271a..d72fb5dc25 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/104] 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 d72fb5dc25..3a7841f96d 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 20913f66db..8bdecc5ef3 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 3365954bab..d088dad2f0 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/104] 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 3a7841f96d..acf29d6530 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 4132fd647f..12fc3dfeda 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 a185f63077..94da384d2d 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/104] 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 acf29d6530..f1bffa8c3a 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/104] 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 f1bffa8c3a..b88705ad36 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 0cfe34508e..610d10e720 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/104] 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 b88705ad36..b7081b2104 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 94da384d2d..5451c4e92e 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/104] 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 12fc3dfeda..be11c7f894 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/104] 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 b7081b2104..abdeec66e4 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/104] 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 abdeec66e4..84ae365082 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 610d10e720..0c9b3b98ed 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/104] 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 d1a671330a..8aa9e23cb9 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 c220f2ee01..b8c2757a62 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 9c6738ece3..af1aae669f 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 8183214e95..08fa72642b 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/104] 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 84ae365082..e602bbd76c 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 0c9b3b98ed..e1fded204d 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/104] 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 e602bbd76c..35aec716e8 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/104] 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 056719c198..44e664f8f8 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/104] 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 44e664f8f8..c6ce9da8fe 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 be11c7f894..fad41424f9 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 5451c4e92e..406e51c86c 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/104] 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 e3f7454a76..44fa402025 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 ca27afe45c..cf15aa2cb9 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 c6ce9da8fe..4493fba38e 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 fad41424f9..3f34c37fcd 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 406e51c86c..5bd28d00a6 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/104] 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 4493fba38e..5aba107287 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 e1fded204d..209d025fbb 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/104] 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 5aba107287..ce1c98a356 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/104] 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 ce1c98a356..45e2623e04 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/104] 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 45e2623e04..7c0186e110 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 209d025fbb..f17293074b 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/104] 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 44fa402025..221bfbcb63 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 cf15aa2cb9..82ef4d1ace 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 7c0186e110..b03fb6cc9b 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/104] 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 221bfbcb63..0005077795 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 82ef4d1ace..d1ddbb1c37 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 b03fb6cc9b..cbbe279346 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/104] 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 cbbe279346..6a4aa40955 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 3f34c37fcd..3b0ace2a34 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 5bd28d00a6..4ef9b7ca76 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/104] 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 a56f331412..cf1939b13d 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/104] 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 a56f331412..ebcd0d9a01 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/104] 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 ebcd0d9a01..2851098f2e 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/104] 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 6a4aa40955..c4bacf0311 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/104] 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 c4bacf0311..b12aeea66c 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/104] 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 453d7eeb5a..299705ce86 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 b12aeea66c..f6b49d1cb4 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 f17293074b..76d8707265 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 0000000000..0cde271d06 --- /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 0000000000..91177fe8a2 --- /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/104] 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 f6b49d1cb4..e54deb9081 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/104] 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 e54deb9081..2992b957f0 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 76d8707265..2bf50ce5e0 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 0cde271d06..78ae673efd 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 91177fe8a2..78b1395dce 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/104] 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 299705ce86..2ca1998e31 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 427c0e99fc..f4573d3c4e 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 f8f5a0efcd..573ef879af 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 2992b957f0..14e6c7aa14 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 2bf50ce5e0..5c5a63ebe5 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 78b1395dce..aa7afda800 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 fd0a5ec595..3e85d1d884 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 c1c97bd2e6..a000f661bf 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 0000000000..d2986bbf42 --- /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 0000000000..c3dc9cd2db --- /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 3b0ace2a34..18c9bb2f19 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 4ef9b7ca76..a3cd3ef910 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/104] 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 2851098f2e..4156bb9983 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/104] 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 298829c01b..90325e2117 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 5c5a63ebe5..ca4d053317 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 d2986bbf42..46b6121588 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/104] 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 90325e2117..7bf9635a4d 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 6057570518..55bf576028 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 56b5daee66..077bf568de 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/104] 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 7bf9635a4d..a0a367d938 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 46b6121588..33f64c0c61 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 c3dc9cd2db..a3765f86bf 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 18c9bb2f19..63cb580dbe 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 a3cd3ef910..41ec82d6c8 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/104] 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 a0a367d938..a323f06d86 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/104] 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 a323f06d86..9bcff93f42 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 ca4d053317..26c78f8c3a 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/104] 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 9bcff93f42..56c9737195 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 26c78f8c3a..5ee2d396aa 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 af1aae669f..0640dc1860 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 08fa72642b..1fd920be33 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/104] 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 4156bb9983..f209a1f7c9 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/104] 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 56c9737195..9a85eacb37 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 0640dc1860..100a532b89 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 1fd920be33..dd6b9ca129 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/104] 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 f209a1f7c9..1c191606d3 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/104] 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 0005077795..65aa5a333b 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 d1ddbb1c37..fc0f0a0a3c 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 f303b17e50..1c1ce77567 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 9a85eacb37..c9b493b98e 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 5ee2d396aa..7e7c2aa161 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 bc80285ea0..f5c315b30e 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 3e85d1d884..55a1627197 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 33f64c0c61..0f2f1706ae 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 a3765f86bf..bb2600c28f 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/104] 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 78ae673efd..1974e917cb 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/104] 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 6deda777e5..88ccde4bb1 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 f4e44f975069bcf08531d0d15e9b793f85d5ea41 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 8 Mar 2023 18:42:27 +0100 Subject: [PATCH 054/104] 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 02f613c155..8ca0199d0f 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 a59c029b8f..0cba55d3c2 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 3795c24491..df2bde469e 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 4ddb36b9e6..33029aac53 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 055/104] 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 8ca0199d0f..a6b420607b 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 33029aac53..42e652437c 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 056/104] 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 42e652437c..ad2f9da3c7 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 057/104] 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 ad2f9da3c7..4ee24235d2 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 058/104] 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 a6b420607b..8db49e5593 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 4ee24235d2..8a1bacb0ba 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 059/104] 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 8db49e5593..e42c4babb2 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 8a1bacb0ba..38e3d34009 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 060/104] 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 38e3d34009..0ef6043e1d 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 061/104] 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 e42c4babb2..a6e5e1fb4a 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 0cba55d3c2..b3d071c9d2 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 df2bde469e..9fbbe378a1 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 0ef6043e1d..2bd19ba5a2 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 062/104] 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 2bd19ba5a2..68ddb7ba91 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 063/104] 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 68ddb7ba91..5efad85b7a 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 064/104] 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 5efad85b7a..8dbf96b262 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 065/104] 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 8dbf96b262..b16dea1796 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 066/104] 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 a3eba01485..370fa3ba2f 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 b16dea1796..ccdc7a914b 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 067/104] 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 6017f393e7..073907c8ce 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 068/104] 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 b91d1559f1..7faa794354 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 069/104] 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 5e1574e60d..6fdec8b14b 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 08fa794810..3fd9f21bc2 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 88ccde4bb1..bc0b5e8110 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 7e7c2aa161..730b7354ca 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 0f2f1706ae..f32113698b 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 070/104] 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 370fa3ba2f..a51091be19 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 071/104] 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 a51091be19..4030e381a2 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 072/104] 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 ccdc7a914b..5e07ebc69d 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 073/104] 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 073907c8ce..6374766cd0 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 074/104] 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 075/104] 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 ac12bef706..97d18c1be5 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 aaa11d678f..1776d5e953 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 076/104] 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 b0f811ac44..b574c95b37 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 077/104] 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 de3ae943f9..413a3445ae 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 d11cd9514b..fd558eb2ce 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 078/104] 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 fc2e27bf8c..8d180c4fa2 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 defc8be3fd..02c8959b92 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 079/104] 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 8d180c4fa2..233a3e1900 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 22a9ecedfa..4a335c75a7 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 02c8959b92..e89dd26240 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 080/104] 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 5e07ebc69d..b72a2bacb7 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 081/104] 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 97d18c1be5..05970389cf 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 1776d5e953..3fb3aee3d9 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 082/104] 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 1974e917cb..45c76887c3 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 f32113698b..4a48ced29f 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 63cb580dbe..80a7167554 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 083/104] 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 260e82e81c..34594240f5 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 7acec17716..a38960324c 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 5b0ecff6e4..2bd5f555b5 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 e7bea48199..028c7ce0a5 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 084/104] 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 7962f0376b..2d11322831 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 085/104] 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 0000000000..4fcf959f66 --- /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 086/104] 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 49e15b3d03..f96cee2476 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 34592e1e55..70cb4dcf14 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 087/104] 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 f96cee2476..0cf915db16 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 088/104] 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 b72a2bacb7..86cb16e2d0 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 089/104] 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 e89dd26240..a64de49eec 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 090/104] 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 5235fd72ed..c26197d27a 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 fc184be774..a3f0515586 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 8ad6b243ff..5248e089a8 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 091/104] 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 c26197d27a..cb0760d4df 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 092/104] 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 80a7167554..62722b0769 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 201e9d493f1607c8e8162ab541f52fc2f207e797 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:32:21 +0100 Subject: [PATCH 093/104] 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 f95d748ba0..e369e0f400 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 d9dfc7db22..656bc0b538 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 094/104] 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 4a48ced29f..ecee0a4e06 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 095/104] 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 cb0760d4df..5f8624ad30 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 5248e089a8..e40657d160 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 096/104] 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 62722b0769..f8f4442411 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 097/104] 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 bc0b5e8110..695399ab20 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 f8f4442411..b2b7588555 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 098/104] 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 21678bfcdf..2136e8edbd 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 099/104] 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 695399ab20..dd18cb878a 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 730b7354ca..ebbdf616cc 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 100/104] 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 05970389cf..77fdf5a83b 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 3fb3aee3d9..f57a8110d7 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 fbcc1ab27603a741246ef3762df67345cfed2c78 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Wed, 22 Mar 2023 13:23:07 +0100 Subject: [PATCH 101/104] 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 86cb16e2d0..aa1216a0fb 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 102/104] 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 1c191606d3..2c862d5a5e 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 103/104] 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 5f8624ad30..6ecb522339 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 e40657d160..320b004aea 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 104/104] 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 e841a5c437..65549d2e8b 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 6ecb522339..78ded40c4a 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 a3f0515586..39c33206c6 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 320b004aea..ee1461baf8 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"); + } +}