From 2057d0ce9a9007f03f9c0519f56ef893600338e5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Mar 2023 13:05:57 +0200 Subject: [PATCH 1/4] SPE-1610 - Modified SceneRaycaster to keep the current selection when the user clicks and drag a selected volume --- src/slic3r/GUI/SceneRaycaster.cpp | 55 ++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 1e8bb0c82..1f44a07d6 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -3,6 +3,8 @@ #include "Camera.hpp" #include "GUI_App.hpp" +#include "Selection.hpp" +#include "Plater.hpp" namespace Slic3r { namespace GUI { @@ -88,10 +90,48 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr item) SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const { + // helper class used to return currently selected volume as hit when overlapping with other volumes + // to allow the user to click and drag on a selected volume + class VolumeKeeper + { + std::optional m_selected_volume_id; + Vec3f m_closest_hit_pos{ std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + bool m_selected_volume_already_found{ false }; + + public: + VolumeKeeper() { + const Selection& selection = wxGetApp().plater()->get_selection(); + if (selection.is_single_volume() || selection.is_single_modifier()) { + const GLVolume* volume = selection.get_first_volume(); + if (!volume->is_wipe_tower && !volume->is_sla_pad() && !volume->is_sla_support()) + m_selected_volume_id = *selection.get_volume_idxs().begin(); + } + } + + bool is_active() const { return m_selected_volume_id.has_value(); } + const Vec3f& get_closest_hit_pos() const { return m_closest_hit_pos; } + bool check_hit_result(const HitResult& hit) { + assert(is_active()); + + if (m_selected_volume_already_found && hit.type == SceneRaycaster::EType::Volume && hit.position.isApprox(m_closest_hit_pos)) + return false; + + if (hit.type == SceneRaycaster::EType::Volume) + m_selected_volume_already_found = *m_selected_volume_id == decode_id(hit.type, hit.raycaster_id); + + m_closest_hit_pos = hit.position; + return true; + } + }; + + VolumeKeeper volume_keeper; + double closest_hit_squared_distance = std::numeric_limits::max(); - auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) { + auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) { const double hit_squared_distance = (camera.get_position() - hit.cast()).squaredNorm(); - const bool ret = hit_squared_distance < closest_hit_squared_distance; + bool ret = hit_squared_distance < closest_hit_squared_distance; + if (volume_keeper.is_active()) + ret |= hit.isApprox(volume_keeper.get_closest_hit_pos()); if (ret) closest_hit_squared_distance = hit_squared_distance; return ret; @@ -103,7 +143,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came HitResult ret; - auto test_raycasters = [this, is_closest, clipping_plane](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { + auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; const std::vector>* raycasters = get_raycasters(type); const Vec3f camera_forward = camera.get_dir_forward().cast(); @@ -117,9 +157,14 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) { current_hit.position = (trafo * current_hit.position.cast()).cast(); current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast()).normalized().cast(); - if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f){ + if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) { if (is_closest(camera, current_hit.position)) { - ret = current_hit; + if (volume_keeper.is_active()) { + if (volume_keeper.check_hit_result(current_hit)) + ret = current_hit; + } + else + ret = current_hit; } } } From 745a45408143af16e1dbf5f74c7c405dd9762472 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Apr 2023 10:23:47 +0200 Subject: [PATCH 2/4] Follow-up of 81cebe5be3903e3cdcaa75f689dbdffaf7891b21 - Fixed crash at startup when any of the svg files used as icon into imgui dialog (and/or splash screen icon) is renamed/removed. --- src/slic3r/GUI/GUI_App.cpp | 6 +++++- src/slic3r/GUI/ImGuiWrapper.cpp | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 01288867a..dc4bb17e8 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -216,7 +216,11 @@ public: // load bitmap for logo BitmapCache bmp_cache; int logo_size = lround(width * 0.25); - wxBitmap logo_bmp = *bmp_cache.load_svg(wxGetApp().logo_name(), logo_size, logo_size); + wxBitmap* logo_bmp_ptr = bmp_cache.load_svg(wxGetApp().logo_name(), logo_size, logo_size); + if (logo_bmp_ptr == nullptr) + return; + + wxBitmap logo_bmp = *logo_bmp_ptr; wxCoord margin = int(m_scale * 20); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 73a4cc439..8e3d2ac5f 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1684,6 +1684,10 @@ void ImGuiWrapper::init_font(bool compress) assert(rect->Width == icon_sz); assert(rect->Height == icon_sz); std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); + if (raw_data.empty()) { + rect_id++; + continue; + } const ImU32* pIn = (ImU32*)raw_data.data(); for (int y = 0; y < icon_sz; y++) { ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); @@ -1720,6 +1724,10 @@ void ImGuiWrapper::init_font(bool compress) assert(rect->Width == icon_sz); assert(rect->Height == icon_sz); std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); + if (raw_data.empty()) { + rect_id++; + continue; + } const ImU32* pIn = (ImU32*)raw_data.data(); for (int y = 0; y < icon_sz; y++) { ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); From 368d36089be366bc608652624622c2911c698476 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Apr 2023 10:56:49 +0200 Subject: [PATCH 3/4] Follow-up of 745a45408143af16e1dbf5f74c7c405dd9762472 - Refactoring in ImGuiWrapper::init_font() to remove duplicated code --- src/slic3r/GUI/ImGuiWrapper.cpp | 58 +++++++++------------------------ 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8e3d2ac5f..4cce6e263 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1678,64 +1678,36 @@ void ImGuiWrapper::init_font(bool compress) int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - // Fill rectangles from the SVG-icons - for (auto icon : font_icons) { + auto load_icon_from_svg = [this, &io, pixels, width, &rect_id](const std::pair icon, int icon_sz) { if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { assert(rect->Width == icon_sz); assert(rect->Height == icon_sz); std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); - if (raw_data.empty()) { - rect_id++; - continue; - } - const ImU32* pIn = (ImU32*)raw_data.data(); - for (int y = 0; y < icon_sz; y++) { - ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); - for (int x = 0; x < icon_sz; x++) - *pOut++ = *pIn++; + if (!raw_data.empty()) { + const ImU32* pIn = (ImU32*)raw_data.data(); + for (int y = 0; y < icon_sz; y++) { + ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); + for (int x = 0; x < icon_sz; x++) + *pOut++ = *pIn++; + } } } rect_id++; + }; + + // Fill rectangles from the SVG-icons + for (auto icon : font_icons) { + load_icon_from_svg(icon, icon_sz); } icon_sz *= 2; // default size of large icon is 32 px for (auto icon : font_icons_large) { - if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { - assert(rect->Width == icon_sz); - assert(rect->Height == icon_sz); - std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); - if (raw_data.empty()) { - rect_id++; - continue; - } - const ImU32* pIn = (ImU32*)raw_data.data(); - for (int y = 0; y < icon_sz; y++) { - ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); - for (int x = 0; x < icon_sz; x++) - *pOut++ = *pIn++; - } - } - rect_id++; + load_icon_from_svg(icon, icon_sz); } icon_sz *= 2; // default size of extra large icon is 64 px for (auto icon : font_icons_extra_large) { - if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { - assert(rect->Width == icon_sz); - assert(rect->Height == icon_sz); - std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); - if (raw_data.empty()) { - rect_id++; - continue; - } - const ImU32* pIn = (ImU32*)raw_data.data(); - for (int y = 0; y < icon_sz; y++) { - ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); - for (int x = 0; x < icon_sz; x++) - *pOut++ = *pIn++; - } - } - rect_id++; + load_icon_from_svg(icon, icon_sz); } // Upload texture to graphics system From de69696edb7e5b70c5d73f36a0081a7a4112ec19 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 4 Apr 2023 11:08:18 +0200 Subject: [PATCH 4/4] General rework. Updated start g-code. https://github.com/prusa3d/PrusaSlicer/pull/10140 --- resources/profiles/Rigid3D.idx | 2 ++ resources/profiles/Rigid3D.ini | 42 +++++++++++++++++----------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/resources/profiles/Rigid3D.idx b/resources/profiles/Rigid3D.idx index 21c2f4b0c..dd7ebab60 100644 --- a/resources/profiles/Rigid3D.idx +++ b/resources/profiles/Rigid3D.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.6.0-alpha5 +1.0.1 General rework. Fix start gcodes. min_slic3r_version = 2.6.0-alpha0 1.0.0 Initial Rigid3D bundle diff --git a/resources/profiles/Rigid3D.ini b/resources/profiles/Rigid3D.ini index 058386293..e33e79e3b 100644 --- a/resources/profiles/Rigid3D.ini +++ b/resources/profiles/Rigid3D.ini @@ -5,7 +5,7 @@ name = Rigid3D # 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.0 +config_version = 1.0.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Rigid3D/ # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -64,7 +64,7 @@ bridge_angle = 0 bridge_flow_ratio = 1 bridge_speed = 60 brim_separation = 0 -brim_type = no_brim +brim_type = outer_only brim_width = 0 clip_multipart_objects = 1 compatible_printers = @@ -75,19 +75,19 @@ dont_support_bridges = 1 draft_shield = disabled elefant_foot_compensation = 0 ensure_vertical_shell_thickness = 0 -external_perimeter_extrusion_width = 0.45 +external_perimeter_extrusion_width = 0 external_perimeter_speed = 50% external_perimeters_first = 1 extra_perimeters = 1 extruder_clearance_height = 20 extruder_clearance_radius = 20 -extrusion_width = 0.45 +extrusion_width = 0 fill_angle = 45 fill_density = 10% fill_pattern = line first_layer_acceleration = 0 first_layer_acceleration_over_raft = 0 -first_layer_extrusion_width = 0.42 +first_layer_extrusion_width = 0 first_layer_height = 0.2 first_layer_speed = 50% first_layer_speed_over_raft = 30 @@ -105,8 +105,8 @@ infill_anchor = 2.5 infill_anchor_max = 12 infill_every_layers = 1 infill_extruder = 1 -infill_extrusion_width = 0.45 -infill_first = 1 +infill_extrusion_width = 0 +infill_first = 0 infill_only_where_needed = 0 infill_overlap = 25% infill_speed = 60 @@ -124,11 +124,11 @@ mmu_segmented_region_max_width = 0 notes = 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_{initial_filament_type}_{printer_model}.gcode overhangs = 1 perimeter_acceleration = 0 perimeter_extruder = 1 -perimeter_extrusion_width = 0.45 +perimeter_extrusion_width = 0 perimeter_speed = 60 perimeters = 2 post_process = @@ -139,7 +139,7 @@ raft_first_layer_density = 90% raft_first_layer_expansion = 3 raft_layers = 0 resolution = 0 -seam_position = rear +seam_position = random single_extruder_multi_material_priming = 1 skirt_distance = 6 skirt_height = 1 @@ -147,14 +147,14 @@ skirts = 0 slice_closing_radius = 0.049 slicing_mode = regular small_perimeter_speed = 15 -solid_infill_below_area = 70 +solid_infill_below_area = 0 solid_infill_every_layers = 0 solid_infill_extruder = 1 -solid_infill_extrusion_width = 0.45 +solid_infill_extrusion_width = 0 solid_infill_speed = 100% spiral_vase = 0 standby_temperature_delta = -5 -support_material = 0 +support_material = 1 support_material_angle = 0 support_material_auto = 1 support_material_bottom_contact_distance = 0 @@ -164,7 +164,7 @@ support_material_closing_radius = 2 support_material_contact_distance = 0.2 support_material_enforce_layers = 0 support_material_extruder = 1 -support_material_extrusion_width = 0.35 +support_material_extrusion_width = 0 support_material_interface_contact_loops = 0 support_material_interface_extruder = 1 support_material_interface_layers = 3 @@ -174,7 +174,7 @@ support_material_interface_speed = 100% support_material_pattern = rectilinear support_material_spacing = 2.5 support_material_speed = 60 -support_material_style = grid +support_material_style = organic support_material_synchronize_layers = 0 support_material_threshold = 0 support_material_with_sheath = 1 @@ -182,7 +182,7 @@ support_material_xy_spacing = 50% thick_bridges = 0 thin_walls = 1 top_fill_pattern = monotonic -top_infill_extrusion_width = 0.4 +top_infill_extrusion_width = 0 top_solid_infill_speed = 100% top_solid_min_thickness = 0.8 travel_speed = 80 @@ -408,7 +408,7 @@ pause_print_gcode = M0 printer_settings_id = printer_technology = FFF printer_variant = 0.4 -remaining_times = 1 +remaining_times = 0 retract_before_travel = 2 retract_before_wipe = 0% retract_layer_change = 0 @@ -438,7 +438,7 @@ bed_shape = 0x0,200x0,200x200,0x200 max_print_height = 190 printer_model = Zero2 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_RIGID3D\nPRINTER_MODEL_ZERO2\nPRINTER_HAS_HEATEDBED\n -start_gcode = G21\nG92 E0\nG28\nM420 S1\nM107\nG90\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X190.0 Y0.1 Z0.3 F1500.0 E15\nG1 X190 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E30\nG1 Z2.0 F1500.0\nG92 E0\n +start_gcode = G21\nG92 E0\nM140 S[first_layer_bed_temperature]\nM104 S[first_layer_temperature]\nG28\nM420 S1\nM107\nG90\nM190 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X190.0 Y0.1 Z0.3 F1500.0 E15\nG1 X190 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E30\nG1 Z2.0 F1500.0\nG92 E0\n end_gcode = G1 X0 Y180\nM107\nG91\nG0 Z20\nT0\nG1 E-1\nM104 T0 S0\nG90\nG92 E0\nM140 S0\nM84\nM300 S2093 P150\nM300 S2637 P150\nM300 S3135 P150\nM300 S4186 P150\nM300 S3135 P150\nM300 S2637 P150\nM300 S2793 P150\nM300 S2349 P150\nM300 S1975 P150\nM300 S2093 P450\n [printer:Rigid3D Zero3] @@ -447,7 +447,7 @@ bed_shape = 0x0,200x0,200x200,0x200 max_print_height = 200 printer_model = Zero3 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_RIGID3D\nPRINTER_MODEL_ZERO3\nPRINTER_HAS_HEATEDBED\n -start_gcode = G21\nG92 E0\nG28\nM420 S1\nM107\nG90\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X190.0 Y0.1 Z0.3 F1500.0 E15\nG1 X190 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E30\nG1 Z2.0 F1500.0\nG92 E0\n +start_gcode = G21\nG92 E0\nM140 S[first_layer_bed_temperature]\nM104 S[first_layer_temperature]\nG28\nM420 S1\nM107\nG90\nM190 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X190.0 Y0.1 Z0.3 F1500.0 E15\nG1 X190 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E30\nG1 Z2.0 F1500.0\nG92 E0\n end_gcode = G92 E0\nT0\nG1 F1800 E-2\nG27 P2\nM107\nM104 T0 S0\nM140 S0\nG90\nG92 E0\nM18\n [printer:Rigid3D Mucit] @@ -456,7 +456,7 @@ bed_shape = 0x0,150x0,150x150,0x150 max_print_height = 150 printer_model = Mucit 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_RIGID3D\nPRINTER_MODEL_MUCIT\n -start_gcode = G21\nG92 E0\nG28\nM420 S1\nM107\nG90\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X140.0 Y0.1 Z0.3 F1500.0 E10\nG1 X140 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E20\nG1 Z2.0 F1500.0\nG92 E0\n +start_gcode = G21\nG92 E0\nM140 S[first_layer_bed_temperature]\nM104 S[first_layer_temperature]\nG28\nM420 S1\nM107\nG90\nM190 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X140.0 Y0.1 Z0.3 F1500.0 E10\nG1 X140 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E20\nG1 Z2.0 F1500.0\nG92 E0\n end_gcode = G1 X0 Y140\nM107\nG91\nG0 Z20\nT0\nG1 E-2\nM104 T0 S0\nG90\nG92 E0\nM140 S0\nM84\nM300 S2093 P150\nM300 S2637 P150\nM300 S3135 P150\nM300 S4186 P150\nM300 S3135 P150\nM300 S2637 P150\nM300 S2793 P150\nM300 S2349 P150\nM300 S1975 P150\nM300 S2093 P450\n [printer:Rigid3D Mucit2] @@ -465,5 +465,5 @@ bed_shape = 0x0,150x0,150x150,0x150 max_print_height = 150 printer_model = Mucit2 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_RIGID3D\nPRINTER_MODEL_MUCIT2\nPRINTER_HAS_HEATEDBED\n -start_gcode = G21\nG92 E0\nG28\nM420 S1\nM107\nG90\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X140.0 Y0.1 Z0.3 F1500.0 E10\nG1 X140 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E20\nG1 Z2.0 F1500.0\nG92 E0\n +start_gcode = G21\nG92 E0\nM140 S[first_layer_bed_temperature]\nM104 S[first_layer_temperature]\nG28\nM420 S1\nM107\nG90\nM190 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nG1 X10.0 Y0.1 Z0.3 F3000.0\nG1 X140.0 Y0.1 Z0.3 F1500.0 E10\nG1 X140 Y0.4 Z0.3 F3000.0\nG1 X10.0 Y0.4 Z0.3 F1500.0 E20\nG1 Z2.0 F1500.0\nG92 E0\n end_gcode = G92 E0\nT0\nG1 F1800 E-2\nG27 P2\nM107\nM104 T0 S0\nM140 S0\nG90\nG92 E0\nM18\n