From a2b2cf743e8e62e20499c02c4c95dcb272d21f2d Mon Sep 17 00:00:00 2001 From: QIDI TECH <893239786@qq.com> Date: Fri, 1 Sep 2023 20:05:08 +0800 Subject: [PATCH] Optimized PressureAdvance --- resources/calib/PressureAdvance/pa_line.stl | Bin 126684 -> 684 bytes .../calib/PressureAdvance/pa_pattern.stl | Bin 126684 -> 684 bytes src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/calib.cpp | 919 ------------------ src/libslic3r/calib.hpp | 281 ------ src/slic3r/GUI/MainFrame.cpp | 6 +- src/slic3r/GUI/Plater.cpp | 171 ++-- src/slic3r/GUI/Plater.hpp | 8 +- src/slic3r/GUI/calib_dlg.cpp | 255 ++--- src/slic3r/GUI/calib_dlg.hpp | 29 +- 10 files changed, 212 insertions(+), 1459 deletions(-) delete mode 100644 src/libslic3r/calib.cpp delete mode 100644 src/libslic3r/calib.hpp diff --git a/resources/calib/PressureAdvance/pa_line.stl b/resources/calib/PressureAdvance/pa_line.stl index 0df26de76a59b0bd97fa4103495cd67c5329d14b..a17a94840f5f9614d4866652a4c885120f24d352 100644 GIT binary patch literal 684 zcmb`E(GkKh3_~?QhUg@4*Zy{>?7=X6@C!Q;A%vBDM1 z;})9vb|_zO66vD0YazO*eihM<*YmuXmKGr$J-)=Ymn*d^0j%xFTGTUP*N5o(TV39Q zDw)7=b83E*&=&H9KZ0fwBhr<7Qzd!I%^oY^X_KxKnMjxKV*7I%>V!Z literal 126684 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 VEN-9ckHnf}t)jxKHuqt%{QozVyIcSO diff --git a/resources/calib/PressureAdvance/pa_pattern.stl b/resources/calib/PressureAdvance/pa_pattern.stl index 0df26de76a59b0bd97fa4103495cd67c5329d14b..a17a94840f5f9614d4866652a4c885120f24d352 100644 GIT binary patch literal 684 zcmb`E(GkKh3_~?QhUg@4*Zy{>?7=X6@C!Q;A%vBDM1 z;})9vb|_zO66vD0YazO*eihM<*YmuXmKGr$J-)=Ymn*d^0j%xFTGTUP*N5o(TV39Q zDw)7=b83E*&=&H9KZ0fwBhr<7Qzd!I%^oY^X_KxKnMjxKV*7I%>V!Z literal 126684 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 VEN-9ckHnf}t)jxKHuqt%{QozVyIcSO diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 3f432ae..0de0b4e 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -35,8 +35,6 @@ set(SLIC3R_SOURCES BuildVolume.cpp BuildVolume.hpp BoostAdapter.hpp - calib.cpp - calib.hpp clipper.cpp clipper.hpp ClipperUtils.cpp diff --git a/src/libslic3r/calib.cpp b/src/libslic3r/calib.cpp deleted file mode 100644 index 24435df..0000000 --- a/src/libslic3r/calib.cpp +++ /dev/null @@ -1,919 +0,0 @@ -#include "calib.hpp" -#include "BoundingBox.hpp" -#include "Config.hpp" -#include "Model.hpp" -#include - -namespace Slic3r { - -// Calculate the optimal Pressure Advance speed -float CalibPressureAdvance::find_optimal_PA_speed(const DynamicPrintConfig &config, double line_width, double layer_height, - int filament_idx) { - const double general_suggested_min_speed = 100.0; - double filament_max_volumetric_speed = config.option("filament_max_volumetric_speed")->get_at(0); - Flow pattern_line = Flow(line_width, layer_height, config.option("nozzle_diameter")->get_at(0)); - auto pa_speed = std::min(std::max(general_suggested_min_speed,config.option("outer_wall_speed")->value), filament_max_volumetric_speed / pattern_line.mm3_per_mm()); - - return std::floor(pa_speed); -} - -std::string CalibPressureAdvance::move_to(Vec2d pt, GCodeWriter& writer, std::string comment) -{ - std::stringstream gcode; - - gcode << writer.retract(); - gcode << writer.travel_to_xy(pt, comment); - gcode << writer.unretract(); - - m_last_pos = Vec3d(pt.x(), pt.y(), 0); - - return gcode.str(); -} - -double CalibPressureAdvance::e_per_mm( - double line_width, - double layer_height, - float nozzle_diameter, - float filament_diameter, - float print_flow_ratio -) const -{ - const Flow line_flow = Flow(line_width, layer_height, nozzle_diameter); - const double filament_area = M_PI * std::pow(filament_diameter / 2, 2); - - return line_flow.mm3_per_mm() / filament_area * print_flow_ratio; -} - -std::string CalibPressureAdvance::convert_number_to_string(double num) const -{ - auto sNumber = std::to_string(num); - sNumber.erase(sNumber.find_last_not_of('0') + 1, std::string::npos); - sNumber.erase(sNumber.find_last_not_of('.') + 1, std::string::npos); - - return sNumber; -} - -std::string CalibPressureAdvance::draw_digit( - double startx, - double starty, - char c, - CalibPressureAdvance::DrawDigitMode mode, - double line_width, - double e_per_mm, - GCodeWriter& writer -) -{ - const double len = m_digit_segment_len; - const double gap = line_width / 2.0; - - const auto dE = e_per_mm * len; - const auto two_dE = dE * 2; - - Vec2d p0, p1, p2, p3, p4, p5; - Vec2d p0_5, p4_5; - Vec2d gap_p0_toward_p3, gap_p2_toward_p3; - Vec2d dot_direction; - - if (mode == CalibPressureAdvance::DrawDigitMode::Bottom_To_Top) { - // 1-------2-------5 - // | | | - // | | | - // 0-------3-------4 - p0 = Vec2d(startx, starty); - p0_5 = Vec2d(startx, starty + len / 2); - p1 = Vec2d(startx, starty + len); - p2 = Vec2d(startx + len, starty + len); - p3 = Vec2d(startx + len, starty); - p4 = Vec2d(startx + len * 2, starty); - p4_5 = Vec2d(startx + len * 2, starty + len / 2); - p5 = Vec2d(startx + len * 2, starty + len); - - gap_p0_toward_p3 = p0 + Vec2d(gap, 0); - gap_p2_toward_p3 = p2 + Vec2d(0, gap); - - dot_direction = Vec2d(-len / 2, 0); - } else { - // 0-------1 - // | | - // 3-------2 - // | | - // 4-------5 - p0 = Vec2d(startx, starty); - p0_5 = Vec2d(startx + len / 2, starty); - p1 = Vec2d(startx + len, starty); - p2 = Vec2d(startx + len, starty - len); - p3 = Vec2d(startx, starty - len); - p4 = Vec2d(startx, starty - len * 2); - p4_5 = Vec2d(startx + len / 2, starty - len * 2); - p5 = Vec2d(startx + len, starty - len * 2); - - gap_p0_toward_p3 = p0 - Vec2d(0, gap); - gap_p2_toward_p3 = p2 - Vec2d(gap, 0); - - dot_direction = Vec2d(0, len / 2); - } - - std::stringstream gcode; - - switch (c) { - case '0': - gcode << move_to(p0, writer, "Glyph: 0"); - gcode << writer.extrude_to_xy(p1, dE); - gcode << writer.extrude_to_xy(p5, two_dE); - gcode << writer.extrude_to_xy(p4, dE); - gcode << writer.extrude_to_xy(gap_p0_toward_p3, two_dE); - break; - case '1': - gcode << move_to(p0_5, writer, "Glyph: 1"); - gcode << writer.extrude_to_xy(p4_5, two_dE); - break; - case '2': - gcode << move_to(p0, writer, "Glyph: 2"); - gcode << writer.extrude_to_xy(p1, dE); - gcode << writer.extrude_to_xy(p2, dE); - gcode << writer.extrude_to_xy(p3, dE); - gcode << writer.extrude_to_xy(p4, dE); - gcode << writer.extrude_to_xy(p5, dE); - break; - case '3': - gcode << move_to(p0, writer, "Glyph: 3"); - gcode << writer.extrude_to_xy(p1, dE); - gcode << writer.extrude_to_xy(p5, two_dE); - gcode << writer.extrude_to_xy(p4, dE); - gcode << move_to(gap_p2_toward_p3, writer); - gcode << writer.extrude_to_xy(p3, dE); - break; - case '4': - gcode << move_to(p0, writer, "Glyph: 4"); - gcode << writer.extrude_to_xy(p3, dE); - gcode << writer.extrude_to_xy(p2, dE); - gcode << move_to(p1, writer); - gcode << writer.extrude_to_xy(p5, two_dE); - break; - case '5': - gcode << move_to(p1, writer, "Glyph: 5"); - gcode << writer.extrude_to_xy(p0, dE); - gcode << writer.extrude_to_xy(p3, dE); - gcode << writer.extrude_to_xy(p2, dE); - gcode << writer.extrude_to_xy(p5, dE); - gcode << writer.extrude_to_xy(p4, dE); - break; - case '6': - gcode << move_to(p1, writer, "Glyph: 6"); - gcode << writer.extrude_to_xy(p0, dE); - gcode << writer.extrude_to_xy(p4, two_dE); - gcode << writer.extrude_to_xy(p5, dE); - gcode << writer.extrude_to_xy(p2, dE); - gcode << writer.extrude_to_xy(p3, dE); - break; - case '7': - gcode << move_to(p0, writer, "Glyph: 7"); - gcode << writer.extrude_to_xy(p1, dE); - gcode << writer.extrude_to_xy(p5, two_dE); - break; - case '8': - gcode << move_to(p2, writer, "Glyph: 8"); - gcode << writer.extrude_to_xy(p3, dE); - gcode << writer.extrude_to_xy(p4, dE); - gcode << writer.extrude_to_xy(p5, dE); - gcode << writer.extrude_to_xy(p1, two_dE); - gcode << writer.extrude_to_xy(p0, dE); - gcode << writer.extrude_to_xy(p3, dE); - break; - case '9': - gcode << move_to(p5, writer, "Glyph: 9"); - gcode << writer.extrude_to_xy(p1, two_dE); - gcode << writer.extrude_to_xy(p0, dE); - gcode << writer.extrude_to_xy(p3, dE); - gcode << writer.extrude_to_xy(p2, dE); - break; - case '.': - gcode << move_to(p4_5, writer, "Glyph: ."); - gcode << writer.extrude_to_xy(p4_5 + dot_direction, dE); - break; - default: - break; - } - - return gcode.str(); -} - -std::string CalibPressureAdvance::draw_number( - double startx, - double starty, - double value, - CalibPressureAdvance::DrawDigitMode mode, - double line_width, - double e_per_mm, - double speed, - GCodeWriter& writer -) -{ - auto sNumber = convert_number_to_string(value); - std::stringstream gcode; - gcode << writer.set_speed(speed); - - for (std::string::size_type i = 0; i < sNumber.length(); ++i) { - if (i > m_max_number_len) { - break; - } - switch (mode) { - case DrawDigitMode::Bottom_To_Top: - gcode << draw_digit( - startx, - starty + i * number_spacing(), - sNumber[i], - mode, - line_width, - e_per_mm, - writer - ); - break; - default: - gcode << draw_digit( - startx + i * number_spacing(), - starty, - sNumber[i], - mode, - line_width, - e_per_mm, - writer - ); - } - } - - return gcode.str(); -} - -std::string CalibPressureAdvanceLine::generate_test(double start_pa /*= 0*/, double step_pa /*= 0.002*/, int count /*= 10*/) -{ - BoundingBoxf bed_ext = get_extents(mp_gcodegen->config().bed_shape.values); - if (is_delta()) { - CalibPressureAdvanceLine::delta_scale_bed_ext(bed_ext); - } - - auto bed_sizes = mp_gcodegen->config().bed_shape.values; - const auto &w = bed_ext.size().x(); - const auto &h = bed_ext.size().y(); - count = std::min(count, int((h - 10) / m_space_y)); - - m_length_long = 40 + std::min(w - 120.0, 0.0); - - auto startx = (w - m_length_short * 2 - m_length_long - 20) / 2; - auto starty = (h - count * m_space_y) / 2; - if (is_delta()) { - CalibPressureAdvanceLine::delta_modify_start(startx, starty, count); - } - - return print_pa_lines(startx, starty, start_pa, step_pa, count); -} - -bool CalibPressureAdvanceLine::is_delta() const -{ - return mp_gcodegen->config().bed_shape.values.size() > 4; -} - -std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double start_y, double start_pa, double step_pa, int num) -{ - auto& writer = mp_gcodegen->writer(); - const auto& config = mp_gcodegen->config(); - - const auto filament_diameter = config.filament_diameter.get_at(0); - const auto print_flow_ratio = 1; - - const double e_per_mm = CalibPressureAdvance::e_per_mm( - m_line_width, - m_height_layer, - m_nozzle_diameter, - filament_diameter, - print_flow_ratio - ); - const double thin_e_per_mm = CalibPressureAdvance::e_per_mm( - m_thin_line_width, - m_height_layer, - m_nozzle_diameter, - filament_diameter, - print_flow_ratio - ); - const double number_e_per_mm = CalibPressureAdvance::e_per_mm( - m_number_line_width, - m_height_layer, - m_nozzle_diameter, - filament_diameter, - print_flow_ratio - ); - - const double fast = CalibPressureAdvance::speed_adjust(m_fast_speed); - const double slow = CalibPressureAdvance::speed_adjust(m_slow_speed); - std::stringstream gcode; - gcode << mp_gcodegen->writer().travel_to_z(m_height_layer); - double y_pos = start_y; - - // prime line - auto prime_x = start_x - 2; - gcode << move_to(Vec2d(prime_x, y_pos + (num - 4) * m_space_y), writer); - gcode << writer.set_speed(slow); - gcode << writer.extrude_to_xy(Vec2d(prime_x, y_pos + 3 * m_space_y), e_per_mm * m_space_y * num * 1.1); - - for (int i = 0; i < num; ++i) { - gcode << writer.set_pressure_advance(start_pa + i * step_pa); - gcode << move_to(Vec2d(start_x, y_pos + i * m_space_y), writer); - gcode << writer.set_speed(slow); - gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short, y_pos + i * m_space_y), e_per_mm * m_length_short); - gcode << writer.set_speed(fast); - gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + i * m_space_y), e_per_mm * m_length_long); - gcode << writer.set_speed(slow); - gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long + m_length_short, y_pos + i * m_space_y), e_per_mm * m_length_short); - } - gcode << writer.set_pressure_advance(0.0); - - if (m_draw_numbers) { - // draw indicator lines - gcode << writer.set_speed(fast); - gcode << move_to(Vec2d(start_x + m_length_short, y_pos + (num - 1) * m_space_y + 2), writer); - gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short, y_pos + (num - 1) * m_space_y + 7), thin_e_per_mm * 7); - gcode << move_to(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 7), writer); - gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 2), thin_e_per_mm * 7); - - for (int i = 0; i < num; i += 2) { - gcode << draw_number( - start_x + m_length_short + m_length_long + m_length_short + 3, - y_pos + i * m_space_y + m_space_y / 2, - start_pa + i * step_pa, - m_draw_digit_mode, - m_number_line_width, - number_e_per_mm, - 3600, - writer - ); - } - } - return gcode.str(); -} - -void CalibPressureAdvanceLine::delta_modify_start(double& startx, double& starty, int count) -{ - startx = -startx; - starty = -(count * m_space_y) / 2; -} - -CalibPressureAdvancePattern::CalibPressureAdvancePattern( - const Calib_Params& params, - const DynamicPrintConfig& config, - bool is_bbl_machine, - Model& model, - const Vec3d& origin -) : - m_params(params) -{ - this->m_draw_digit_mode = DrawDigitMode::Bottom_To_Top; - - refresh_setup(config, is_bbl_machine, model, origin); -}; - -void CalibPressureAdvancePattern::generate_custom_gcodes( - const DynamicPrintConfig& config, - bool is_bbl_machine, - Model& model, - const Vec3d& origin -) -{ - std::stringstream gcode; - gcode << "; start pressure advance pattern for layer\n"; - - refresh_setup(config, is_bbl_machine, model, origin); - - gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), m_writer, "Move to start XY position"); - gcode << m_writer.travel_to_z(height_first_layer(), "Move to start Z position"); - gcode << m_writer.set_pressure_advance(m_params.start); - - const DrawBoxOptArgs default_box_opt_args(*this); - - // create anchoring frame - gcode << draw_box( - m_starting_point.x(), - m_starting_point.y(), - print_size_x(), - frame_size_y(), - default_box_opt_args - ); - - // create tab for numbers - DrawBoxOptArgs draw_box_opt_args = default_box_opt_args; - draw_box_opt_args.is_filled = true; - draw_box_opt_args.num_perimeters = wall_count(); - gcode << draw_box( - m_starting_point.x(), - m_starting_point.y() + frame_size_y() + line_spacing_first_layer(), - glyph_tab_max_x() - m_starting_point.x(), - max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, - draw_box_opt_args - ); - - std::vector gcode_items; - const DrawLineOptArgs default_line_opt_args(*this); - const int num_patterns = get_num_patterns(); // "cache" for use in loops - - // draw pressure advance pattern - for (int i = 0; i < m_num_layers; ++i) { - if (i > 0) { - gcode << "; end pressure advance pattern for layer\n"; - CustomGCode::Item item; - item.print_z = height_first_layer() + (i - 1) * height_layer(); - item.type = CustomGCode::Type::Custom; - item.extra = gcode.str(); - gcode_items.push_back(item); - - gcode = std::stringstream(); // reset for next layer contents - gcode << "; start pressure advance pattern for layer\n"; - - const double layer_height = height_first_layer() + (i * height_layer()); - gcode << m_writer.travel_to_z(layer_height, "Move to layer height"); - } - - // line numbering - if (i == 1) { - gcode << m_writer.set_pressure_advance(m_params.start); - - double number_e_per_mm = e_per_mm( - line_width(), - height_layer(), - m_config.option("nozzle_diameter")->get_at(0), - m_config.option("filament_diameter")->get_at(0), - m_config.option("filament_flow_ratio")->get_at(0) - ); - - // glyph on every other line - for (int j = 0; j < num_patterns; j += 2) { - gcode << draw_number( - glyph_start_x(j), - m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(), - m_params.start + (j * m_params.step), - m_draw_digit_mode, - line_width(), - number_e_per_mm, - speed_first_layer(), - m_writer - ); - } - } - - DrawLineOptArgs draw_line_opt_args = default_line_opt_args; - - double to_x = m_starting_point.x() + pattern_shift(); - double to_y = m_starting_point.y(); - double side_length = m_wall_side_length; - - // shrink first layer to fit inside frame - if (i == 0) { - double shrink = - ( - line_spacing_first_layer() * (wall_count() - 1) + - (line_width_first_layer() * (1 - m_encroachment)) - ) / std::sin(to_radians(m_corner_angle) / 2) - ; - side_length = m_wall_side_length - shrink; - to_x += shrink * std::sin(to_radians(90) - to_radians(m_corner_angle) / 2); - to_y += - line_spacing_first_layer() * (wall_count() - 1) + - (line_width_first_layer() * (1 - m_encroachment)) - ; - } - - double initial_x = to_x; - double initial_y = to_y; - - gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to pattern start"); - - for (int j = 0; j < num_patterns; ++j) { - // increment pressure advance - gcode << m_writer.set_pressure_advance(m_params.start + (j * m_params.step)); - - for (int k = 0; k < wall_count(); ++k) { - to_x += std::cos(to_radians(m_corner_angle) / 2) * side_length; - to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length; - - draw_line_opt_args = default_line_opt_args; - draw_line_opt_args.height = i == 0 ? height_first_layer() : height_layer(); - draw_line_opt_args.line_width = line_width(); // don't use line_width_first_layer so results are consistent across all layers - draw_line_opt_args.speed = i == 0 ? speed_adjust(speed_first_layer()) : speed_adjust(speed_perimeter()); - draw_line_opt_args.comment = "Print pattern wall"; - gcode << draw_line(Vec2d(to_x, to_y), draw_line_opt_args); - - to_x -= std::cos(to_radians(m_corner_angle) / 2) * side_length; - to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length; - - gcode << draw_line(Vec2d(to_x, to_y), draw_line_opt_args); - - to_y = initial_y; - if (k != wall_count() - 1) { - // perimeters not done yet. move to next perimeter - to_x += line_spacing_angle(); - gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to start next pattern wall"); - } else if (j != num_patterns - 1) { - // patterns not done yet. move to next pattern - to_x += m_pattern_spacing + line_width(); - gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to next pattern"); - } else if (i != m_num_layers - 1) { - // layers not done yet. move back to start - to_x = initial_x; - gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move back to start position"); - } else { - // everything done - } - } - } - } - - gcode << m_writer.set_pressure_advance(m_params.start); - gcode << "; end pressure advance pattern for layer\n"; - - CustomGCode::Item item; - item.print_z = max_layer_z(); - item.type = CustomGCode::Type::Custom; - item.extra = gcode.str(); - gcode_items.push_back(item); - - CustomGCode::Info info; - info.mode = CustomGCode::Mode::SingleExtruder; - info.gcodes = gcode_items; - - //model.plates_custom_gcodes[model.curr_plate_index] = info; -} - -void CalibPressureAdvancePattern::refresh_setup( - const DynamicPrintConfig& config, - bool is_bbl_machine, - const Model& model, - const Vec3d& origin -) -{ - m_config = config; - m_config.apply(model.objects.front()->config.get(), true); - m_config.apply(model.objects.front()->volumes.front()->config.get(), true); - - m_is_delta = (m_config.option("bed_shape")->values.size() > 4); - - _refresh_starting_point(model); - _refresh_writer(is_bbl_machine, model, origin); -} - -void CalibPressureAdvancePattern::_refresh_starting_point(const Model& model) -{ - ModelObject* obj = model.objects.front(); - //BoundingBoxf3 bbox = - // obj->instance_bounding_box( - // *obj->instances.front(), - // false - // ) - //; - - m_starting_point = Vec3d(0, 0, 0); - m_starting_point.y() += m_handle_spacing; - - if (m_is_delta) { - m_starting_point.x() *= -1; - m_starting_point.y() -= (frame_size_y() / 2); - } -} - -void CalibPressureAdvancePattern::_refresh_writer( - bool is_bbl_machine, - const Model& model, - const Vec3d& origin -) -{ - PrintConfig print_config; - print_config.apply(m_config, true); - - m_writer.apply_print_config(print_config); - //m_writer.set_xy_offset(origin(0), origin(1)); - - const unsigned int extruder_id = model.objects.front()->volumes.front()->extruder_id(); - m_writer.set_extruders({ extruder_id }); - m_writer.set_extruder(extruder_id); -} - -std::string CalibPressureAdvancePattern::draw_line( - Vec2d to_pt, - DrawLineOptArgs opt_args -) -{ - const double e_per_mm = CalibPressureAdvance::e_per_mm( - opt_args.line_width, - opt_args.height, - m_config.option("nozzle_diameter")->get_at(0), - m_config.option("filament_diameter")->get_at(0), - m_config.option("filament_flow_ratio")->get_at(0) - ); - - const double length = get_distance(Vec2d(m_last_pos.x(), m_last_pos.y()), to_pt); - auto dE = e_per_mm * length; - - std::stringstream gcode; - - gcode << m_writer.set_speed(opt_args.speed); - gcode << m_writer.extrude_to_xy(to_pt, dE, opt_args.comment); - - m_last_pos = Vec3d(to_pt.x(), to_pt.y(), 0); - - return gcode.str(); -} - -std::string CalibPressureAdvancePattern::draw_box( - double min_x, - double min_y, - double size_x, - double size_y, - DrawBoxOptArgs opt_args -) -{ - std::stringstream gcode; - - double x = min_x; - double y = min_y; - const double max_x = min_x + size_x; - const double max_y = min_y + size_y; - - const double spacing = opt_args.line_width - opt_args.height * (1 - M_PI / 4); - - // if number of perims exceeds size of box, reduce it to max - const int max_perimeters = - std::min( - // this is the equivalent of number of perims for concentric fill - std::floor(size_x * std::sin(to_radians(45))) / (spacing / std::sin(to_radians(45))), - std::floor(size_y * std::sin(to_radians(45))) / (spacing / std::sin(to_radians(45))) - ) - ; - - opt_args.num_perimeters = std::min(opt_args.num_perimeters, max_perimeters); - - gcode << move_to(Vec2d(min_x, min_y), m_writer, "Move to box start"); - - DrawLineOptArgs line_opt_args(*this); - line_opt_args.height = opt_args.height; - line_opt_args.line_width = opt_args.line_width; - line_opt_args.speed = opt_args.speed; - - for (int i = 0; i < opt_args.num_perimeters; ++i) { - if (i != 0) { // after first perimeter, step inwards to start next perimeter - x += spacing; - y += spacing; - gcode << move_to(Vec2d(x, y), m_writer, "Step inwards to print next perimeter"); - } - - y += size_y - i * spacing * 2; - line_opt_args.comment = "Draw perimeter (up)"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - - x += size_x - i * spacing * 2; - line_opt_args.comment = "Draw perimeter (right)"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - - y -= size_y - i * spacing * 2; - line_opt_args.comment = "Draw perimeter (down)"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - - x -= size_x - i * spacing * 2; - line_opt_args.comment = "Draw perimeter (left)"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } - - if (!opt_args.is_filled) { - return gcode.str(); - } - - // create box infill - const double spacing_45 = spacing / std::sin(to_radians(45)); - - const double bound_modifier = - (spacing * (opt_args.num_perimeters - 1)) + - (opt_args.line_width * (1 - m_encroachment)) - ; - const double x_min_bound = min_x + bound_modifier; - const double x_max_bound = max_x - bound_modifier; - const double y_min_bound = min_y + bound_modifier; - const double y_max_bound = max_y - bound_modifier; - const int x_count = std::floor((x_max_bound - x_min_bound) / spacing_45); - const int y_count = std::floor((y_max_bound - y_min_bound) / spacing_45); - - double x_remainder = std::fmod((x_max_bound - x_min_bound), spacing_45); - double y_remainder = std::fmod((y_max_bound - y_min_bound), spacing_45); - - x = x_min_bound; - y = y_min_bound; - - gcode << move_to(Vec2d(x, y), m_writer, "Move to fill start"); - - for (int i = 0; i < x_count + y_count + (x_remainder + y_remainder >= spacing_45 ? 1 : 0); ++i) { // this isn't the most robust way, but less expensive than finding line intersections - if (i < std::min(x_count, y_count)) { - if (i % 2 == 0) { - x += spacing_45; - y = y_min_bound; - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right"); - - y += x - x_min_bound; - x = x_min_bound; - line_opt_args.comment = "Fill: Print up/left"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } else { - y += spacing_45; - x = x_min_bound; - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up"); - - x += y - y_min_bound; - y = y_min_bound; - line_opt_args.comment = "Fill: Print down/right"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } - } else if (i < std::max(x_count, y_count)) { - if (x_count > y_count) { - // box is wider than tall - if (i % 2 == 0) { - x += spacing_45; - y = y_min_bound; - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right"); - - x -= y_max_bound - y_min_bound; - y = y_max_bound; - line_opt_args.comment = "Fill: Print up/left"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } else { - if (i == y_count) { - x += spacing_45 - y_remainder; - y_remainder = 0; - } else { - x += spacing_45; - } - y = y_max_bound; - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right"); - - x += y_max_bound - y_min_bound; - y = y_min_bound; - line_opt_args.comment = "Fill: Print down/right"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } - } else { - // box is taller than wide - if (i % 2 == 0) { - x = x_max_bound; - if (i == x_count) { - y += spacing_45 - x_remainder; - x_remainder = 0; - } else { - y += spacing_45; - } - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up"); - - x = x_min_bound; - y += x_max_bound - x_min_bound; - line_opt_args.comment = "Fill: Print up/left"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } else { - x = x_min_bound; - y += spacing_45; - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up"); - - x = x_max_bound; - y -= x_max_bound - x_min_bound; - line_opt_args.comment = "Fill: Print down/right"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } - } - } else { - if (i % 2 == 0) { - x = x_max_bound; - if (i == x_count) { - y += spacing_45 - x_remainder; - } else { - y += spacing_45; - } - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step up"); - - x -= y_max_bound - y; - y = y_max_bound; - line_opt_args.comment = "Fill: Print up/left"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } else { - if (i == y_count) { - x += spacing_45 - y_remainder; - } else { - x += spacing_45; - } - y = y_max_bound; - gcode << move_to(Vec2d(x, y), m_writer, "Fill: Step right"); - - y -= x_max_bound - x; - x = x_max_bound; - line_opt_args.comment = "Fill: Print down/right"; - gcode << draw_line(Vec2d(x, y), line_opt_args); - } - } - } - - return gcode.str(); -} - -double CalibPressureAdvancePattern::get_distance(Vec2d from, Vec2d to) const -{ - return std::hypot((to.x() - from.x()), (to.y() - from.y())); -} - -double CalibPressureAdvancePattern::object_size_x() const -{ - return get_num_patterns() * ((wall_count() - 1) * line_spacing_angle()) + - (get_num_patterns() - 1) * (m_pattern_spacing + line_width()) + - std::cos(to_radians(m_corner_angle) / 2) * m_wall_side_length + - line_spacing_first_layer() * wall_count() - ; -} - -double CalibPressureAdvancePattern::object_size_y() const -{ - return 2 * (std::sin(to_radians(m_corner_angle) / 2) * m_wall_side_length) + - max_numbering_height() + - m_glyph_padding_vertical * 2 + - line_width_first_layer(); -} - -double CalibPressureAdvancePattern::glyph_start_x(int pattern_i) const -{ - // note that pattern_i is zero-based! - // align glyph's start with first perimeter of specified pattern - double x = - // starting offset - m_starting_point.x() + - pattern_shift() + - - // width of pattern extrusions - pattern_i * (wall_count() - 1) * line_spacing_angle() + // center to center distance of extrusions - pattern_i * line_width() + // endcaps. center to end on either side = 1 line width - - // space between each pattern - pattern_i * m_pattern_spacing - ; - - // align to middle of pattern walls - x += wall_count() * line_spacing_angle() / 2; - - // shift so glyph is centered on pattern - // m_digit_segment_len = half of X length of glyph - x -= (glyph_length_x() / 2); - - return x; -} - -double CalibPressureAdvancePattern::glyph_length_x() const -{ - // half of line_width sticks out on each side - return line_width() + (2 * m_digit_segment_len); -} - -double CalibPressureAdvancePattern::glyph_tab_max_x() const -{ - // only every other glyph is shown, starting with 1 - int num = get_num_patterns(); - int max_num = - (num % 2 == 0) - ? num - 1 - : num - ; - - // padding at end should be same as padding at start - double padding = glyph_start_x(0) - m_starting_point.x(); - - return - glyph_start_x(max_num - 1) + // glyph_start_x is zero-based - (glyph_length_x() - line_width() / 2) + - padding - ; -} - -double CalibPressureAdvancePattern::max_numbering_height() const -{ - std::string::size_type most_characters = 0; - const int num_patterns = get_num_patterns(); - - // note: only every other number is printed - for (std::string::size_type i = 0; i < num_patterns; i += 2) { - std::string sNumber = convert_number_to_string(m_params.start + (i * m_params.step)); - - if (sNumber.length() > most_characters) { - most_characters = sNumber.length(); - } - } - - most_characters = std::min(most_characters, m_max_number_len); - - return (most_characters * m_digit_segment_len) + ((most_characters - 1) * m_digit_gap_len); -} - -double CalibPressureAdvancePattern::pattern_shift() const -{ - return - (wall_count() - 1) * line_spacing_first_layer() + - line_width_first_layer() + - m_glyph_padding_horizontal - ; -} -} // namespace Slic3r diff --git a/src/libslic3r/calib.hpp b/src/libslic3r/calib.hpp deleted file mode 100644 index 56d32bc..0000000 --- a/src/libslic3r/calib.hpp +++ /dev/null @@ -1,281 +0,0 @@ -#pragma once -#define calib_pressure_advance_dd - -#include "GCode.hpp" -#include "GCodeWriter.hpp" -#include "PrintConfig.hpp" - -namespace Slic3r { - -enum class CalibMode : int { - Calib_None = 0, - Calib_PA_Line, - Calib_PA_Pattern, - Calib_PA_Tower, - Calib_Temp_Tower, - Calib_Vol_speed_Tower, - Calib_VFA_Tower, - Calib_Retraction_tower, - Calib_FRF -}; - -struct Calib_Params { - Calib_Params() : mode(CalibMode::Calib_None) { }; - double start, end, step; - bool print_numbers; - CalibMode mode; -}; - -class CalibPressureAdvance { - public: - static float find_optimal_PA_speed(const DynamicPrintConfig &config, double line_width, double layer_height, - int filament_idx = 0); - - protected: - CalibPressureAdvance() =default; - ~CalibPressureAdvance() =default; - - enum class DrawDigitMode { - Left_To_Right, - Bottom_To_Top - }; - - void delta_scale_bed_ext(BoundingBoxf& bed_ext) const { bed_ext.scale(1.0f / 1.41421f); } - - std::string move_to(Vec2d pt, GCodeWriter& writer, std::string comment = std::string()); - double e_per_mm( - double line_width, - double layer_height, - float nozzle_diameter, - float filament_diameter, - float print_flow_ratio - ) const; - double speed_adjust(int speed) const { return speed * 60; }; - - std::string convert_number_to_string(double num) const; - double number_spacing() const { return m_digit_segment_len + m_digit_gap_len; }; - std::string draw_digit( - double startx, - double starty, - char c, - CalibPressureAdvance::DrawDigitMode mode, - double line_width, - double e_per_mm, - GCodeWriter& writer - ); - std::string draw_number( - double startx, - double starty, - double value, - CalibPressureAdvance::DrawDigitMode mode, - double line_width, - double e_per_mm, - double speed, - GCodeWriter& writer - ); - - Vec3d m_last_pos; - - DrawDigitMode m_draw_digit_mode {DrawDigitMode::Left_To_Right}; - const double m_digit_segment_len {2}; - const double m_digit_gap_len {1}; - const std::string::size_type m_max_number_len {5}; -}; - -class CalibPressureAdvanceLine : public CalibPressureAdvance { -public: - CalibPressureAdvanceLine(GCode* gcodegen) : - mp_gcodegen(gcodegen), - m_nozzle_diameter(gcodegen->config().nozzle_diameter.get_at(0)) - { }; - ~CalibPressureAdvanceLine() { }; - - std::string generate_test(double start_pa = 0, double step_pa = 0.002, int count = 50); - - void set_speed(double fast = 100.0, double slow = 20.0) { - m_slow_speed = slow; - m_fast_speed = fast; - } - - const double& line_width() { return m_line_width; }; - bool is_delta() const; - bool& draw_numbers() { return m_draw_numbers; } - -private: - std::string print_pa_lines(double start_x, double start_y, double start_pa, double step_pa, int num); - - void delta_modify_start(double& startx, double& starty, int count); - - GCode* mp_gcodegen; - - double m_nozzle_diameter; - double m_slow_speed, m_fast_speed; - - const double m_height_layer {0.2}; - const double m_line_width {0.6}; - const double m_thin_line_width {0.44}; - const double m_number_line_width {0.48}; - const double m_space_y {3.5}; - - double m_length_short {20.0}, m_length_long {40.0}; - bool m_draw_numbers {true}; -}; - -struct SuggestedConfigCalibPAPattern { - const std::vector> float_pairs { - {"initial_layer_print_height", 0.25}, - {"layer_height", 0.2}, - {"initial_layer_speed", 30} - }; - - const std::vector> nozzle_ratio_pairs { - {"line_width", 112.5}, - {"initial_layer_line_width", 140} - }; - - const std::vector> int_pairs { - {"skirt_loops", 0}, - {"wall_loops", 3} - }; - - const std::pair brim_pair {"brim_type", BrimType::btNoBrim}; -}; - -class CalibPressureAdvancePattern : public CalibPressureAdvance { -friend struct DrawLineOptArgs; -friend struct DrawBoxOptArgs; - -public: - CalibPressureAdvancePattern( - const Calib_Params& params, - const DynamicPrintConfig& config, - bool is_bbl_machine, - Model& model, - const Vec3d& origin - ); - - double handle_xy_size() const { return m_handle_xy_size; }; - double handle_spacing() const { return m_handle_spacing; }; - double print_size_x() const { return object_size_x() + pattern_shift(); }; - double print_size_y() const { return object_size_y(); }; - double max_layer_z() const { return height_first_layer() + ((m_num_layers - 1) * height_layer()); }; - - void generate_custom_gcodes( - const DynamicPrintConfig& config, - bool is_bbl_machine, - Model& model, - const Vec3d& origin - ); - -protected: - double speed_first_layer() const { return m_config.option("initial_layer_speed")->value; }; - double speed_perimeter() const { return m_config.option("outer_wall_speed")->value; }; - double line_width_first_layer() const { return m_config.get_abs_value("initial_layer_line_width"); }; - double line_width() const { return m_config.get_abs_value("line_width"); }; - int wall_count() const { return m_config.option("wall_loops")->value; }; - -private: - struct DrawLineOptArgs { - DrawLineOptArgs(const CalibPressureAdvancePattern& p) : - height {p.height_layer()}, - line_width {p.line_width()}, - speed {p.speed_adjust(p.speed_perimeter())} - { }; - - double height; - double line_width; - double speed; - std::string comment {"Print line"}; - }; - - struct DrawBoxOptArgs { - DrawBoxOptArgs(const CalibPressureAdvancePattern& p) : - num_perimeters {p.wall_count()}, - height {p.height_first_layer()}, - line_width {p.line_width_first_layer()}, - speed {p.speed_adjust(p.speed_first_layer())} - { }; - - bool is_filled {false}; - int num_perimeters; - double height; - double line_width; - double speed; - }; - - void refresh_setup( - const DynamicPrintConfig& config, - bool is_bbl_machine, - const Model& model, - const Vec3d& origin - ); - void _refresh_starting_point(const Model& model); - void _refresh_writer( - bool is_bbl_machine, - const Model& model, - const Vec3d& origin - ); - - double height_first_layer() const { return m_config.option("initial_layer_print_height")->value; }; - double height_layer() const { return m_config.option("layer_height")->value; }; - const int get_num_patterns() const - { - return std::ceil((m_params.end - m_params.start) / m_params.step + 1); - } - - std::string draw_line( - Vec2d to_pt, - DrawLineOptArgs opt_args - ); - std::string draw_box( - double min_x, - double min_y, - double size_x, - double size_y, - DrawBoxOptArgs opt_args - ); - - double to_radians(double degrees) const { return degrees * M_PI / 180; }; - double get_distance(Vec2d from, Vec2d to) const; - - /* - from slic3r documentation: spacing = extrusion_width - layer_height * (1 - PI/4) - "spacing" = center-to-center distance of adjacent extrusions, which partially overlap - https://manual.slic3r.org/advanced/flow-math - https://ellis3dp.com/Print-Tuning-Guide/articles/misconceptions.html#two-04mm-perimeters--08mm - */ - double line_spacing() const { return line_width() - height_layer() * (1 - M_PI / 4); }; - double line_spacing_first_layer() const { return line_width_first_layer() - height_first_layer() * (1 - M_PI / 4); }; - double line_spacing_angle() const { return line_spacing() / std::sin(to_radians(m_corner_angle) / 2); }; - - double object_size_x() const; - double object_size_y() const; - double frame_size_y() const { return std::sin(to_radians(double(m_corner_angle) / 2)) * m_wall_side_length * 2; }; - - double glyph_start_x(int pattern_i = 0) const; - double glyph_length_x() const; - double glyph_tab_max_x() const; - double max_numbering_height() const; - - double pattern_shift() const; - - const Calib_Params& m_params; - - DynamicPrintConfig m_config; - GCodeWriter m_writer; - bool m_is_delta; - Vec3d m_starting_point; - - const double m_handle_xy_size {5}; - const double m_handle_spacing {2}; - const int m_num_layers {4}; - - const double m_wall_side_length {30.0}; - const int m_corner_angle {90}; - const int m_pattern_spacing {2}; - const double m_encroachment {1. / 3.}; - - const double m_glyph_padding_horizontal {1}; - const double m_glyph_padding_vertical {1}; -}; -} // namespace Slic3r diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 3b33bbe..ee58490 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1579,8 +1579,7 @@ void MainFrame::init_menubar_as_editor() append_menu_item(flowrateMenu, wxID_ANY, _L("Fine"), _L("Flow Rate Fine"), [this](wxCommandEvent &) { - if (!m_frf_calib_dlg) - m_frf_calib_dlg = new FRF_Calibration_Dlg((wxWindow *) this, wxID_ANY, m_plater); + m_frf_calib_dlg = new FRF_Calibration_Dlg((wxWindow *) this, wxID_ANY, m_plater); m_frf_calib_dlg->ShowModal(); }, "", nullptr, [this]() { return m_plater->is_view3D_shown(); }, this); @@ -1590,8 +1589,7 @@ void MainFrame::init_menubar_as_editor() append_menu_item(calibrationMenu, wxID_ANY, _L("Pressure advance"), _L("Pressure advance"), [this](wxCommandEvent &) { - if (!m_pa_calib_dlg) - m_pa_calib_dlg = new PA_Calibration_Dlg((wxWindow *) this, wxID_ANY, m_plater); + m_pa_calib_dlg = new PA_Calibration_Dlg((wxWindow *) this, wxID_ANY, m_plater); m_pa_calib_dlg->ShowModal(); }, "", nullptr, [this]() { return m_plater->is_view3D_shown(); }, this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0e56f7f..ee029c4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -5369,14 +5370,11 @@ void Plater::calib_flowrate_coarse() { new_project(); wxGetApp().mainframe->select_tab(size_t(0)); - Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); - Tab *tab_filament = wxGetApp().get_tab(Preset::TYPE_FILAMENT); - Tab *tab_printer = wxGetApp().get_tab(Preset::TYPE_PRINTER); - DynamicPrintConfig new_config; + DynamicPrintConfig new_config; new_config.set_key_value("complete_objects", new ConfigOptionBool(true)); new_config.set_key_value("extruder_clearance_radius", new ConfigOptionFloat(1)); - new_config.set_key_value("extrusion_multiplier", new ConfigOptionFloats{1.}); + new_config.set_key_value("extrusion_multiplier", new ConfigOptionFloats{1}); new_config.set_key_value("between_objects_gcode", new ConfigOptionString("{if current_object_idx==1}M221 S105{endif}" "{if current_object_idx==2}M221 S110{endif}" @@ -5386,6 +5384,10 @@ void Plater::calib_flowrate_coarse() "{if current_object_idx==6}M221 S90{endif}" "{if current_object_idx==7}M221 S85{endif}" "{if current_object_idx==8}M221 S80{endif}")); + + Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); + Tab *tab_filament = wxGetApp().get_tab(Preset::TYPE_FILAMENT); + Tab *tab_printer = wxGetApp().get_tab(Preset::TYPE_PRINTER); tab_print->load_config(new_config); tab_filament->load_config(new_config); tab_printer->load_config(new_config); @@ -5398,33 +5400,33 @@ void Plater::calib_flowrate_coarse() get_notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::PrintInfoNotificationLevel, message); } -void Plater::calib_flowrate_fine(const Calib_Params ¶ms) +void Plater::calib_flowrate_fine(const double target_extrusion_multiplier) { new_project(); wxGetApp().mainframe->select_tab(size_t(0)); - if (params.mode != CalibMode::Calib_FRF) - return; + + auto printerConfig = &wxGetApp().preset_bundle->printers.get_edited_preset().config; + int em = target_extrusion_multiplier * 100; + auto start_gcode = printerConfig->opt_string("start_gcode"); + + DynamicPrintConfig new_config; + new_config.set_key_value("complete_objects", new ConfigOptionBool(true)); + new_config.set_key_value("extruder_clearance_radius", new ConfigOptionFloat(1)); + new_config.set_key_value("extrusion_multiplier", new ConfigOptionFloats{1}); + new_config.set_key_value("start_gcode", new ConfigOptionString(start_gcode + "\nM221 S"+std::to_string(em))); + new_config.set_key_value("between_objects_gcode", + new ConfigOptionString("{if current_object_idx==1}M221 S"+std::to_string(em+1)+"{endif}" + "{if current_object_idx==2}M221 S"+std::to_string(em+2)+"{endif}" + "{if current_object_idx==3}M221 S"+std::to_string(em+3)+"{endif}" + "{if current_object_idx==4}M221 S"+std::to_string(em+4)+"{endif}" + "{if current_object_idx==5}M221 S"+std::to_string(em-1)+"{endif}" + "{if current_object_idx==6}M221 S"+std::to_string(em-2)+"{endif}" + "{if current_object_idx==7}M221 S"+std::to_string(em-3)+"{endif}" + "{if current_object_idx==8}M221 S"+std::to_string(em-4)+"{endif}")); Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); Tab *tab_filament = wxGetApp().get_tab(Preset::TYPE_FILAMENT); Tab *tab_printer = wxGetApp().get_tab(Preset::TYPE_PRINTER); - DynamicPrintConfig new_config; - auto printerConfig = &wxGetApp().preset_bundle->printers.get_edited_preset().config; - int extru_multip = params.start * 100; - auto start_gcode = printerConfig->opt_string("start_gcode"); - new_config.set_key_value("complete_objects", new ConfigOptionBool(true)); - new_config.set_key_value("extruder_clearance_radius", new ConfigOptionFloat(1)); - new_config.set_key_value("extrusion_multiplier", new ConfigOptionFloats{1.}); - new_config.set_key_value("start_gcode", new ConfigOptionString(start_gcode + "\nM221 S"+std::to_string(extru_multip))); - new_config.set_key_value("between_objects_gcode", - new ConfigOptionString("{if current_object_idx==1}M221 S"+std::to_string(extru_multip+1)+"{endif}" - "{if current_object_idx==2}M221 S"+std::to_string(extru_multip+2)+"{endif}" - "{if current_object_idx==3}M221 S"+std::to_string(extru_multip+3)+"{endif}" - "{if current_object_idx==4}M221 S"+std::to_string(extru_multip+4)+"{endif}" - "{if current_object_idx==5}M221 S"+std::to_string(extru_multip-1)+"{endif}" - "{if current_object_idx==6}M221 S"+std::to_string(extru_multip-2)+"{endif}" - "{if current_object_idx==7}M221 S"+std::to_string(extru_multip-3)+"{endif}" - "{if current_object_idx==8}M221 S"+std::to_string(extru_multip-4)+"{endif}")); tab_print->load_config(new_config); tab_filament->load_config(new_config); tab_printer->load_config(new_config); @@ -5438,46 +5440,95 @@ void Plater::calib_flowrate_fine(const Calib_Params ¶ms) } //B34 -void Plater::calib_pa(const int pa_method, wxString StartPA, wxString EndPA, wxString PAStep) +void Plater::calib_pa_line(const double StartPA, double EndPA, double PAStep) { new_project(); wxGetApp().mainframe->select_tab(size_t(0)); +//Load model std::vector model_path; - //double pa = StartPA; - - switch (pa_method) { - case 0: - { - Tab *tab_printer = wxGetApp().get_tab(Preset::TYPE_PRINTER); - DynamicPrintConfig new_config; - auto printerConfig = &wxGetApp().preset_bundle->printers.get_edited_preset().config; - - auto end_gcode = printerConfig->opt_string("end_gcode"); - std::string set_pa_gcode = "M900 K"; - end_gcode = set_pa_gcode + end_gcode; - new_config.set_key_value("end_gcode", new ConfigOptionString(end_gcode)); - tab_printer->load_config(new_config); - - model_path.emplace_back(Slic3r::resources_dir() + "/calib/PressureAdvance/pa_line.stl"); - load_files(model_path, true, false, false); - break; - } - - case 1: - { - model_path.emplace_back(Slic3r::resources_dir() + "/calib/PressureAdvance/pa_pattern.stl"); - load_files(model_path, true, false, false); - break; - } - - case 2: - { - model_path.emplace_back(Slic3r::resources_dir() + "/calib/PressureAdvance/pa_tower.stl"); - load_files(model_path, true, false, false); - break; - } - default: break; + model_path.emplace_back(Slic3r::resources_dir() + "/calib/PressureAdvance/pa_line.stl"); + load_files(model_path, true, false, false); +//Check step count + const Vec2d plate_center = build_volume().bed_center(); + double count = floor((EndPA - StartPA) / PAStep); + double max_count = floor(plate_center.y() / 2.5) - 2; + if (count > max_count) + { + count = max_count; } + + auto printerConfig = &wxGetApp().preset_bundle->printers.get_edited_preset().config; +//Position aided model + sidebar().obj_manipul()->on_change("position", 0, plate_center.x() - 50); + sidebar().obj_manipul()->set_uniform_scaling(false); + sidebar().obj_manipul()->on_change("size", 1, count * 5); + auto first_layer_height = printerConfig->opt_float("first_layer_height"); + sidebar().obj_manipul()->on_change("size", 2, first_layer_height); + sidebar().obj_manipul()->set_uniform_scaling(true); + +//Generate line gcode + double start_x = plate_center.x() - 40; + double end_x = plate_center.x() + 40; + double start_y = plate_center.y() - count *2.5; + double end_y = plate_center.y() + count *2.5; + std::string pa_line_gcode = "M900 K"; + + +//Generate number gcode + std::string pa_number_gcode = "M900 K"; + + +//Set and load end gcode + auto end_gcode = printerConfig->opt_string("end_gcode"); + end_gcode = pa_line_gcode + pa_number_gcode + end_gcode; + + DynamicPrintConfig new_config; + new_config.set_key_value("end_gcode", new ConfigOptionString(end_gcode)); + + Tab *tab_printer = wxGetApp().get_tab(Preset::TYPE_PRINTER); + tab_printer->load_config(new_config); + + std::string message = _u8L("NOTICE: The calibration function modifies some parameters. After calibration, record the best value and restore the other parameters."); + get_notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::PrintInfoNotificationLevel, message); +} + +void Plater::calib_pa_pattern(const double StartPA, double EndPA, double PAStep) +{ + new_project(); + wxGetApp().mainframe->select_tab(size_t(0)); + + std::vector model_path; + model_path.emplace_back(Slic3r::resources_dir() + "/calib/PressureAdvance/pa_pattern.stl"); + load_files(model_path, true, false, false); + + const Vec2d plate_center = build_volume().bed_center(); + sidebar().obj_manipul()->on_change("position", 0, plate_center.x() - 50); + + Tab *tab_printer = wxGetApp().get_tab(Preset::TYPE_PRINTER); + DynamicPrintConfig new_config; + auto printerConfig = &wxGetApp().preset_bundle->printers.get_edited_preset().config; + + auto end_gcode = printerConfig->opt_string("end_gcode"); + std::string set_pa_gcode = "M900 K"; + end_gcode = set_pa_gcode + end_gcode; + new_config.set_key_value("end_gcode", new ConfigOptionString(end_gcode)); + tab_printer->load_config(new_config); + + std::string message = _u8L("NOTICE: The calibration function modifies some parameters. After calibration, record the best value and restore the other parameters."); + get_notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::PrintInfoNotificationLevel, message); +} + +void Plater::calib_pa_tower(const double StartPA, double EndPA, double PAStep) +{ + new_project(); + wxGetApp().mainframe->select_tab(size_t(0)); + + std::vector model_path; + model_path.emplace_back(Slic3r::resources_dir() + "/calib/PressureAdvance/pa_tower.stl"); + load_files(model_path, true, false, false); + + std::string message = _u8L("NOTICE: The calibration function modifies some parameters. After calibration, record the best value and restore the other parameters."); + get_notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::PrintInfoNotificationLevel, message); } void Plater::import_zip_archive() diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ce271d2..cded433 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -16,8 +16,6 @@ #include "Jobs/Job.hpp" #include "Jobs/Worker.hpp" #include "Search.hpp" -//B34 - #include "libslic3r/Calib.hpp" class wxButton; class ScalableButton; @@ -167,9 +165,11 @@ public: SLAPrint& sla_print(); //B34 - void calib_pa(const int pa_method, wxString StartPA, wxString EndPA, wxString PAStep); + void calib_pa_line(const double StartPA, double EndPA, double PAStep); + void calib_pa_pattern(const double StartPA, double EndPA, double PAStep); + void calib_pa_tower(const double StartPA, double EndPA, double PAStep); void calib_flowrate_coarse(); - void calib_flowrate_fine(const Calib_Params ¶ms); + void calib_flowrate_fine(const double target_extrusion_multiplier); void new_project(); diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index f2504d5..fd75fe6 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -5,91 +5,45 @@ #include #include "MainFrame.hpp" #include +#include +#include namespace Slic3r { namespace GUI { - -wxBoxSizer *create_item_checkbox(wxString title, wxWindow *parent, bool *value, CheckBoxInWT *&checkbox) -{ - wxBoxSizer* m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL); - - m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 5); - - checkbox = new ::CheckBoxInWT(parent); - m_sizer_checkbox->Add(checkbox, 0, wxALIGN_CENTER, 0); - m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 8); - - auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxSize(-1, -1), 0); - checkbox_title->SetForegroundColour(wxColour(144, 144, 144)); - checkbox_title->SetFont(::Label::Body_13); - checkbox_title->Wrap(-1); - m_sizer_checkbox->Add(checkbox_title, 0, wxALIGN_CENTER | wxALL, 3); - - checkbox->SetValue(true); - - checkbox->Bind(wxEVT_TOGGLEBUTTON, [parent, checkbox, value](wxCommandEvent& e) { - (*value) = (*value) ? false : true; - e.Skip(); - }); - - return m_sizer_checkbox; -} FRF_Calibration_Dlg::FRF_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) - : DPIDialog(parent, id, _L("Flowrate Fine Calibration"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxNO_BORDER), m_plater(plater) + : DPIDialog(parent, id, _L("Flowrate Fine Calibration"), wxDefaultPosition, wxSize(-1, 280), wxDEFAULT_DIALOG_STYLE | wxNO_BORDER), m_plater(plater) { wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); SetSizer(v_sizer); - // Settings - auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; - wxString start_length_str = _L("Extrusion Multiplier: "); - auto text_size = wxWindow::GetTextExtent(start_length_str); - text_size.x = text_size.x * 1.5; - wxStaticBoxSizer *settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Settings")); + // desc + std::string setting_desc_message = _u8L("Please input the best value from the coarse calibration to further determine a more accurate extrusion multiplier."); + auto setting_desc = new wxStaticText(this, wxID_ANY, setting_desc_message, wxDefaultPosition, wxSize(340, -1), wxALIGN_LEFT); + setting_desc->Wrap(setting_desc->GetClientSize().x); + v_sizer->Add(setting_desc, 0, wxTOP | wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 15); - auto st_size = FromDIP(wxSize(text_size.x, -1)); - auto ti_size = FromDIP(wxSize(90, -1)); - auto desc_size = FromDIP(wxSize(307, -1)); + // Settings + wxStaticBoxSizer *settings_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _L("Settings")); + + auto extrusion_multiplier_text = new wxStaticText(this, wxID_ANY, _L("Extrusion Multiplier: "), wxDefaultPosition, wxSize(230, -1), wxALIGN_LEFT); + settings_sizer->Add(extrusion_multiplier_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); // extru - auto multip = filament_config->opt_float("extrusion_multiplier",0); - std::string multip_str = std::to_string(multip); - if (multip_str.find(".") > 0) { - size_t fp = multip_str.rfind("."); - size_t f = multip_str.rfind("0"); - while (f > fp) { - if (f != -1) { - multip_str = multip_str.erase(f); - } - f = multip_str.rfind("0"); - } - fp = multip_str.rfind("."); - if (fp == multip_str.size() - 1) { - multip_str = multip_str.erase(fp); - } - } - auto start_length_sizer = new wxBoxSizer(wxHORIZONTAL); - auto start_length_text = new wxStaticText(this, wxID_ANY, start_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiExtru = new wxTextCtrl(this, wxID_ANY, multip_str, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE); - m_tiExtru->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; + auto read_extrusion_multiplier = filament_config->opt_float("extrusion_multiplier", 0); + std::stringstream ss; + ss << std::setprecision(2) << read_extrusion_multiplier; + m_tc_extrusion_multiplier = new wxTextCtrl(this, wxID_ANY, ss.str(), wxDefaultPosition, wxSize(100, -1), wxBORDER_SIMPLE); + m_tc_extrusion_multiplier->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + settings_sizer->Add(m_tc_extrusion_multiplier, 0, wxRIGHT | wxALIGN_RIGHT, 0); - // desc - auto setting_desc = new wxStaticText(this, wxID_ANY, _u8L("Please input the best value from the coarse calibration to further determine a more accurate extrusion multiplier."), - wxDefaultPosition, desc_size, wxALIGN_LEFT); - setting_desc->Wrap(setting_desc->GetClientSize().x); - - // delay - start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(m_tiExtru, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - settings_sizer->Add(start_length_sizer); - v_sizer->Add(setting_desc, 0, wxTOP | wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 15); - //v_sizer->Add(0, FromDIP(10), 0, wxEXPAND, 5); v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 15); - v_sizer->Add(0, FromDIP(5), 0, wxEXPAND, 5); - m_btnStart = new wxButton(this, wxID_OK, _L("OK")); + v_sizer->Add(0, 5, 0, wxEXPAND, 5); + + m_btnStart = new wxButton(this, wxID_ANY, _L("OK")); m_btnStart->Bind(wxEVT_BUTTON, &FRF_Calibration_Dlg::on_start, this); v_sizer->Add(m_btnStart, 0, wxRIGHT | wxALIGN_RIGHT, 15); - v_sizer->Add(0, FromDIP(8), 0, wxEXPAND, 5); + v_sizer->Add(0, 8, 0, wxEXPAND, 5); m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FRF_Calibration_Dlg::on_start), NULL, this); @@ -106,22 +60,22 @@ FRF_Calibration_Dlg::~FRF_Calibration_Dlg() { void FRF_Calibration_Dlg::on_start(wxCommandEvent& event) { bool read_double = false; - read_double = m_tiExtru->GetValue().ToDouble(&m_params.start); + double target_extrusion_multiplier; + read_double = m_tc_extrusion_multiplier->GetValue().ToDouble(&target_extrusion_multiplier); - if (!read_double || m_params.start < 0.9) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\n 0.9 <= Extrusion Multiplier <= 1.1\n"), wxEmptyString, wxICON_WARNING | wxOK); + if (!read_double || target_extrusion_multiplier < 0.5) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\n 0.5 <= Extrusion Multiplier <= 1.5\n"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); - m_tiExtru->SetValue("0.9"); + m_tc_extrusion_multiplier->SetValue("0.5"); return; - } else if (!read_double || m_params.start > 1.1) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\n 0.9 <= Extrusion Multiplier <= 1.1\n"), wxEmptyString, wxICON_WARNING | wxOK); + } else if (!read_double || target_extrusion_multiplier > 1.5) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\n 0.5 <= Extrusion Multiplier <= 1.5\n"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); - m_tiExtru->SetValue("1.1"); + m_tc_extrusion_multiplier->SetValue("1.5"); return; } - m_params.mode = CalibMode::Calib_FRF; - m_plater->calib_flowrate_fine(m_params); + m_plater->calib_flowrate_fine(target_extrusion_multiplier); EndModal(wxID_OK); } @@ -131,83 +85,62 @@ void FRF_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) { } PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) - : DPIDialog(parent, id, _L("Pressure Advance Calibration"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater) + : DPIDialog(parent, id, _L("Pressure Advance Calibration"), wxDefaultPosition, wxSize(-1, 280), wxDEFAULT_DIALOG_STYLE | wxNO_BORDER), m_plater(plater) { wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); SetSizer(v_sizer); - wxBoxSizer* choice_sizer = new wxBoxSizer(wxHORIZONTAL); - choice_sizer->Add(FromDIP(5), 0, 0, wxEXPAND, 5); - wxString m_rbMethodChoices[] = { _L("PA Line"), _L("PA Pattern"), _L("PA Tower") }; - int m_rbMethodNChoices = sizeof(m_rbMethodChoices) / sizeof(wxString); - m_rbMethod = new wxRadioBox(this, wxID_ANY, _L("Method"), wxDefaultPosition, wxDefaultSize, m_rbMethodNChoices, m_rbMethodChoices, 1, wxRA_SPECIFY_COLS); - m_rbMethod->SetSelection(0); - choice_sizer->Add(m_rbMethod, 0, wxALL, 5); - - v_sizer->Add(choice_sizer); + wxString m_rbMethodChoices[] = { _L("PA Line"), _L("PA Pattern"), _L("PA Tower") }; + int m_rbMethodNChoices = sizeof(m_rbMethodChoices) / sizeof(wxString); + m_rbMethod = new wxRadioBox(this, wxID_ANY, _L("Method"), wxDefaultPosition, wxDefaultSize, m_rbMethodNChoices, m_rbMethodChoices, 1, wxRA_SPECIFY_COLS); + m_rbMethod->SetSelection(0); + v_sizer->Add(m_rbMethod, 0, wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 15); // Settings - // - wxString start_pa_str = _L("Start PA: "); - wxString end_pa_str = _L("End PA: "); - wxString PA_step_str = _L("PA step: "); - auto text_size = wxWindow::GetTextExtent(start_pa_str); - text_size.IncTo(wxWindow::GetTextExtent(end_pa_str)); - text_size.IncTo(wxWindow::GetTextExtent(PA_step_str)); - text_size.x = text_size.x * 1.5; - wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Settings")); + wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Settings")); - auto st_size = FromDIP(wxSize(text_size.x, -1)); - auto ti_size = FromDIP(wxSize(90, -1)); // start PA auto start_PA_sizer = new wxBoxSizer(wxHORIZONTAL); - auto start_pa_text = new wxStaticText(this, wxID_ANY, start_pa_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiStartPA = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_CENTRE | wxTE_PROCESS_ENTER); - m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto start_pa_text = new wxStaticText(this, wxID_ANY, _L("Start PA: "), wxDefaultPosition, wxSize(80, -1), wxALIGN_LEFT); + start_PA_sizer->Add(start_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_PA_sizer->Add(start_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_PA_sizer->Add(m_tiStartPA, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + m_tcStartPA = new wxTextCtrl(this, wxID_ANY, "0", wxDefaultPosition, wxSize(100, -1), wxBORDER_SIMPLE); + m_tcStartPA->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + start_PA_sizer->Add(m_tcStartPA, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); settings_sizer->Add(start_PA_sizer); // end PA auto end_PA_sizer = new wxBoxSizer(wxHORIZONTAL); - auto end_pa_text = new wxStaticText(this, wxID_ANY, end_pa_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiEndPA = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_CENTRE | wxTE_PROCESS_ENTER); - m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto end_pa_text = new wxStaticText(this, wxID_ANY, _L("End PA: "), wxDefaultPosition, wxSize(80, -1), wxALIGN_LEFT); end_PA_sizer->Add(end_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - end_PA_sizer->Add(m_tiEndPA, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + + m_tcEndPA = new wxTextCtrl(this, wxID_ANY, "0.04", wxDefaultPosition, wxSize(100, -1), wxBORDER_SIMPLE); + m_tcStartPA->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + end_PA_sizer->Add(m_tcEndPA, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); settings_sizer->Add(end_PA_sizer); // PA step auto PA_step_sizer = new wxBoxSizer(wxHORIZONTAL); - auto PA_step_text = new wxStaticText(this, wxID_ANY, PA_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiPAStep = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_CENTRE | wxTE_PROCESS_ENTER); - m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto PA_step_text = new wxStaticText(this, wxID_ANY, _L("PA step: "), wxDefaultPosition, wxSize(80, -1), wxALIGN_LEFT); PA_step_sizer->Add(PA_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - PA_step_sizer->Add(m_tiPAStep, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + + m_tcPAStep = new wxTextCtrl(this, wxID_ANY, "0.002", wxDefaultPosition, wxSize(100, -1), wxBORDER_SIMPLE); + m_tcStartPA->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + PA_step_sizer->Add(m_tcPAStep, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); settings_sizer->Add(PA_step_sizer); - v_sizer->Add(settings_sizer); - v_sizer->Add(0, FromDIP(10), 0, wxEXPAND, 5); - m_btnStart = new Button(this, _L("OK")); - StateColor btn_bg_green(std::pair(wxColour(51, 91, 188), StateColor::Pressed), - std::pair(wxColour(51, 109, 251), StateColor::Hovered), - std::pair(wxColour(68, 121, 251), StateColor::Normal)); + v_sizer->Add(0, 5, 0, wxEXPAND, 5); + v_sizer->Add(settings_sizer, 0, wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 15); + v_sizer->Add(0, 5, 0, wxEXPAND, 5); - m_btnStart->SetBackgroundColor(btn_bg_green); - m_btnStart->SetBorderColor(wxColour(0, 150, 136)); - m_btnStart->SetTextColor(wxColour("#FFFFFE")); - m_btnStart->SetSize(wxSize(FromDIP(48), FromDIP(24))); - m_btnStart->SetMinSize(wxSize(FromDIP(48), FromDIP(24))); - m_btnStart->SetCornerRadius(FromDIP(3)); - m_btnStart->Bind(wxEVT_BUTTON, &PA_Calibration_Dlg::on_start, this); - v_sizer->Add(m_btnStart, 0, wxALL | wxALIGN_RIGHT, FromDIP(5)); + m_btnStart = new wxButton(this, wxID_ANY, _L("OK")); + m_btnStart->Bind(wxEVT_BUTTON, &PA_Calibration_Dlg::on_start, this); + v_sizer->Add(m_btnStart, 0, wxRIGHT | wxALIGN_RIGHT, 15); + v_sizer->Add(0, 8, 0, wxEXPAND, 5); - PA_Calibration_Dlg::reset_params(); - - // Connect Events m_rbMethod->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_method_changed), NULL, this); - this->Connect(wxEVT_SHOW, wxShowEventHandler(PA_Calibration_Dlg::on_show)); + m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PA_Calibration_Dlg::on_start), NULL, this); + wxGetApp().UpdateDlgDarkUI(this); Layout(); Fit(); @@ -219,58 +152,40 @@ PA_Calibration_Dlg::~PA_Calibration_Dlg() { m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PA_Calibration_Dlg::on_start), NULL, this); } -void PA_Calibration_Dlg::reset_params() { - int method = m_rbMethod->GetSelection(); - - m_tiStartPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.0)); - - switch (method) { - case 1: - m_params.mode = CalibMode::Calib_PA_Pattern; - m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.08)); - m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.005)); - break; - case 2: - m_params.mode = CalibMode::Calib_PA_Tower; - m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.1)); - m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.002)); - break; - default: - m_params.mode = CalibMode::Calib_PA_Line; - m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.1)); - m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.002)); - break; - } -} - void PA_Calibration_Dlg::on_start(wxCommandEvent& event) { bool read_double = false; - read_double = m_tiStartPA->GetTextCtrl()->GetValue().ToDouble(&m_params.start); - read_double = read_double && m_tiEndPA->GetTextCtrl()->GetValue().ToDouble(&m_params.end); - read_double = read_double && m_tiPAStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step); - if (!read_double || m_params.start < 0 || m_params.step < EPSILON || m_params.end < m_params.start + m_params.step) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart PA: >= 0.0\nEnd PA: > Start PA\nPA step: >= 0.001)"), wxEmptyString, wxICON_WARNING | wxOK); + double start_pa; + double end_pa; + double pa_step; + read_double = m_tcStartPA->GetValue().ToDouble(&start_pa); + read_double = read_double && m_tcEndPA->GetValue().ToDouble(&end_pa); + read_double = read_double && m_tcPAStep->GetValue().ToDouble(&pa_step); + if (!read_double || start_pa < 0 || pa_step < EPSILON || end_pa < start_pa + pa_step) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart PA: >= 0.0\nEnd PA: > Start PA + PA step\nPA step: >= 0.001)"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } switch (m_rbMethod->GetSelection()) { - case 1: - m_params.mode = CalibMode::Calib_PA_Pattern; + case 0:{ + m_plater->calib_pa_line( start_pa, end_pa, pa_step); break; - case 2: - m_params.mode = CalibMode::Calib_PA_Tower; + } + case 1:{ + m_plater->calib_pa_pattern( start_pa, end_pa, pa_step); break; - default: - m_params.mode = CalibMode::Calib_PA_Line; + } + case 2:{ + m_plater->calib_pa_tower( start_pa, end_pa, pa_step); + break; + } + default: break; } - m_plater->calib_pa(m_rbMethod->GetSelection(), m_tiStartPA->GetTextCtrl()->GetValue(), m_tiEndPA->GetTextCtrl()->GetValue(), m_tiPAStep->GetTextCtrl()->GetValue()); EndModal(wxID_OK); } void PA_Calibration_Dlg::on_method_changed(wxCommandEvent& event) { - PA_Calibration_Dlg::reset_params(); event.Skip(); } @@ -279,8 +194,4 @@ void PA_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } -void PA_Calibration_Dlg::on_show(wxShowEvent& event) { - PA_Calibration_Dlg::reset_params(); -} - }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/calib_dlg.hpp b/src/slic3r/GUI/calib_dlg.hpp index 3f31340..9eb9a36 100644 --- a/src/slic3r/GUI/calib_dlg.hpp +++ b/src/slic3r/GUI/calib_dlg.hpp @@ -13,7 +13,6 @@ #include "GUI_App.hpp" #include "wx/hyperlink.h" #include -#include "libslic3r/calib.hpp" #include "Plater.hpp" namespace Slic3r { namespace GUI { @@ -27,11 +26,10 @@ public: protected: virtual void on_start(wxCommandEvent &event); - Calib_Params m_params; - wxTextCtrl *m_tiExtru; - wxButton * m_btnStart; - Plater * m_plater; + wxTextCtrl* m_tc_extrusion_multiplier; + wxButton* m_btnStart; + Plater* m_plater; }; class PA_Calibration_Dlg : public DPIDialog @@ -40,20 +38,17 @@ public: PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater); ~PA_Calibration_Dlg(); void on_dpi_changed(const wxRect& suggested_rect) override; - void on_show(wxShowEvent& event); -protected: - void reset_params(); - virtual void on_start(wxCommandEvent& event); - virtual void on_method_changed(wxCommandEvent& event); protected: - Calib_Params m_params; - wxRadioBox* m_rbMethod; - TextInput* m_tiStartPA; - TextInput* m_tiEndPA; - TextInput* m_tiPAStep; - Button* m_btnStart; - Plater* m_plater; + virtual void on_start(wxCommandEvent& event); + virtual void on_method_changed(wxCommandEvent& event); + + wxRadioBox* m_rbMethod; + wxTextCtrl* m_tcStartPA; + wxTextCtrl* m_tcEndPA; + wxTextCtrl* m_tcPAStep; + wxButton* m_btnStart; + Plater* m_plater; }; }} // namespace Slic3r::GUI