This commit is contained in:
bubnikv 2019-11-14 17:02:46 +01:00
commit 8c377c6585
28 changed files with 552 additions and 59 deletions

View file

@ -145,6 +145,9 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double
return result;
}
// FIXME: needed to fix build on Mac on buildserver
constexpr double FillGyroid::PatternTolerance;
void FillGyroid::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,

View file

@ -285,7 +285,7 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2
return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1)));
}
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z) const
{
if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool)
throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect.");
@ -321,6 +321,15 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
gcode += gcodegen.unretract();
}
double current_z = gcodegen.writer().get_position().z();
if (z == -1.) // in case no specific z was provided, print at current_z pos
z = current_z;
if (! is_approx(z, current_z)) {
gcode += gcodegen.writer().retract();
gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer.");
gcode += gcodegen.writer().unretract();
}
// Process the end filament gcode.
std::string end_filament_gcode_str;
@ -387,16 +396,23 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(end_pos.cast<double>());
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
if (! is_approx(z, current_z)) {
gcode += gcodegen.writer().retract();
gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer.");
gcode += gcodegen.writer().unretract();
}
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
if (new_extruder_id >= 0) {
// Start the wipe at the current position.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left,
end_pos.y())));
else {
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
if (new_extruder_id >= 0) {
// Start the wipe at the current position.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left,
end_pos.y())));
}
}
// Let the planner know we are traveling between objects.
@ -522,7 +538,23 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id,
if (m_layer_idx < (int)m_tool_changes.size()) {
if (! (size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer.");
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id);
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
// resulting in a wipe tower with sparse layers.
double wipe_tower_z = -1;
bool ignore_sparse = false;
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
wipe_tower_z = m_last_wipe_tower_print_z;
ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool);
if (m_tool_change_idx == 0 && ! ignore_sparse)
wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height;
}
if (! ignore_sparse) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z);
m_last_wipe_tower_print_z = wipe_tower_z;
}
}
m_brim_done = true;
}

View file

@ -113,7 +113,7 @@ public:
private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
// Postprocesses gcode: rotates and moves G1 extrusions and returns result
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
@ -134,6 +134,7 @@ private:
int m_tool_change_idx;
bool m_brim_done;
bool i_have_brim = false;
double m_last_wipe_tower_print_z = 0.f;
};
class GCode {

View file

@ -474,6 +474,7 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
m_z_pos(0.f),
m_is_first_layer(false),
m_bridging(float(config.wipe_tower_bridging)),
m_no_sparse_layers(config.wipe_tower_no_sparse_layers),
m_gcode_flavor(config.gcode_flavor),
m_current_tool(initial_tool),
wipe_volumes(wiping_matrix)
@ -1145,9 +1146,10 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON;
box_coordinates box = fill_box;
for (int i=0;i<2;++i) {
if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer
if (! toolchanges_on_layer) {
if (i==0) box.expand(m_perimeter_width);
else box.expand(-m_perimeter_width);
}
@ -1201,9 +1203,12 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
// Ask our writer about how much material was consumed:
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
// Ask our writer about how much material was consumed.
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
if (! m_no_sparse_layers || toolchanges_on_layer)
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result;
result.priming = false;

View file

@ -220,6 +220,7 @@ private:
float m_parking_pos_retraction = 0.f;
float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_no_sparse_layers = false;
bool m_set_extruder_trimpot = false;
bool m_adhesion = true;
GCodeFlavor m_gcode_flavor;

View file

@ -201,6 +201,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "wipe_tower"
|| opt_key == "wipe_tower_width"
|| opt_key == "wipe_tower_bridging"
|| opt_key == "wipe_tower_no_sparse_layers"
|| opt_key == "wiping_volumes_matrix"
|| opt_key == "parking_pos_retraction"
|| opt_key == "cooling_tube_retraction"

View file

@ -1837,6 +1837,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("wipe_tower_no_sparse_layers", coBool);
def->label = L("No sparse layers (EXPERIMENTAL)");
def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no toolchanges. "
"On layers with a toolchange, extruder will travel downward to print the wipe tower. "
"User is responsible for ensuring there is no collision with the print.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("support_material", coBool);
def->label = L("Generate support material");
def->category = L("Support material");

View file

@ -669,6 +669,7 @@ public:
ConfigOptionStrings start_filament_gcode;
ConfigOptionBool single_extruder_multi_material;
ConfigOptionBool single_extruder_multi_material_priming;
ConfigOptionBool wipe_tower_no_sparse_layers;
ConfigOptionString toolchange_gcode;
ConfigOptionFloat travel_speed;
ConfigOptionBool use_firmware_retraction;
@ -739,6 +740,7 @@ protected:
OPT_PTR(retract_speed);
OPT_PTR(single_extruder_multi_material);
OPT_PTR(single_extruder_multi_material_priming);
OPT_PTR(wipe_tower_no_sparse_layers);
OPT_PTR(start_gcode);
OPT_PTR(start_filament_gcode);
OPT_PTR(toolchange_gcode);

View file

@ -204,6 +204,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
std::string cst_texture(custom_texture);
if (!cst_texture.empty())
{
std::replace(cst_texture.begin(), cst_texture.end(), '\\', '/');
if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture))
cst_texture = "";
}
@ -212,6 +213,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
std::string cst_model(custom_model);
if (!cst_model.empty())
{
std::replace(cst_model.begin(), cst_model.end(), '\\', '/');
if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model))
cst_model = "";
}

View file

@ -707,24 +707,24 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
print_volume.min(2) = -1e10;
ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside;
bool all_contained = true;
bool contained_min_one = false;
for (GLVolume* volume : this->volumes)
{
if ((volume == nullptr) || !volume->printable || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
continue;
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
bool contained = print_volume.contains(bb);
all_contained &= contained;
volume->is_outside = !contained;
if (!volume->printable)
continue;
if (contained)
contained_min_one = true;
volume->is_outside = !contained;
if ((state == ModelInstance::PVS_Inside) && volume->is_outside)
state = ModelInstance::PVS_Fully_Outside;
@ -735,7 +735,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
if (out_state != nullptr)
*out_state = state;
return /*all_contained*/ contained_min_one; // #ys_FIXME_delete_after_testing
return contained_min_one;
}
void GLVolumeCollection::reset_outside_state()

View file

@ -12,6 +12,7 @@
#include "boost/nowide/iostream.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <algorithm>
@ -60,7 +61,9 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
{
m_shape = default_pt.values;
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
std::replace(m_custom_texture.begin(), m_custom_texture.end(), '\\', '/');
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
std::replace(m_custom_model.begin(), m_custom_model.end(), '\\', '/');
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
@ -212,7 +215,18 @@ wxPanel* BedShapePanel::init_texture_panel()
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_texture == NONE) ? "" : _(m_custom_texture);
bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture);
lbl->SetForegroundColour(exists ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) : wxColor(*wxRED));
wxString tooltip_text = "";
if (m_custom_texture != NONE)
{
if (!exists)
tooltip_text += _(L("Not found: "));
tooltip_text += _(m_custom_texture);
}
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
@ -280,7 +294,18 @@ wxPanel* BedShapePanel::init_model_panel()
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_model == NONE) ? "" : _(m_custom_model);
bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model);
lbl->SetForegroundColour(exists ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) : wxColor(*wxRED));
wxString tooltip_text = "";
if (m_custom_model != NONE)
{
if (!exists)
tooltip_text += _(L("Not found: "));
tooltip_text += _(m_custom_model);
}
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
@ -521,6 +546,8 @@ void BedShapePanel::load_texture()
return;
}
std::replace(file_name.begin(), file_name.end(), '\\', '/');
wxBusyCursor wait;
m_custom_texture = file_name;
@ -544,6 +571,8 @@ void BedShapePanel::load_model()
return;
}
std::replace(file_name.begin(), file_name.end(), '\\', '/');
wxBusyCursor wait;
m_custom_model = file_name;

View file

@ -166,6 +166,8 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
int max_row_width = 0;
int current_row_width = 0;
bool is_variants = false;
for (const auto &model : models) {
if (! filter(model)) { continue; }
@ -220,6 +222,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:")));
alt_label->SetFont(font_alt_nozzle);
variants_sizer->Add(alt_label, 0, wxBOTTOM, 3);
is_variants = true;
}
auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
@ -280,10 +283,10 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
}
title_sizer->AddStretchSpacer();
if (titles.size() > 1) {
if (/*titles.size() > 1*/is_variants) {
// It only makes sense to add the All / None buttons if there's multiple printers
auto *sel_all_std = new wxButton(this, wxID_ANY, _(L("All standard")));
auto *sel_all_std = new wxButton(this, wxID_ANY, titles.size() > 1 ? _(L("All standard")) : _(L("Standard")));
auto *sel_all = new wxButton(this, wxID_ANY, _(L("All")));
auto *sel_none = new wxButton(this, wxID_ANY, _(L("None")));
sel_all_std->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, false); });

View file

@ -11,6 +11,12 @@
#include <wx/tooltip.h>
#include <boost/algorithm/string/predicate.hpp>
#ifdef __WXOSX__
#define wxOSX true
#else
#define wxOSX false
#endif
namespace Slic3r { namespace GUI {
wxString double_to_string(double const value, const int max_precision /*= 4*/)
@ -304,7 +310,7 @@ void TextCtrl::BUILD() {
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
if (! m_opt.multiline)
if (! m_opt.multiline && !wxOSX)
// Only disable background refresh for single line input fields, as they are completely painted over by the edit control.
// This does not apply to the multi-line edit field, where the last line and a narrow frame around the text is not cleared.
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
@ -491,7 +497,7 @@ void CheckBox::BUILD() {
// Set Label as a string of at least one space simbol to correct system scaling of a CheckBox
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->SetValue(check_value);
if (m_opt.readonly) temp->Disable();
@ -601,7 +607,7 @@ void SpinCtrl::BUILD() {
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
0|wxTE_PROCESS_ENTER, min_val, max_val, default_value);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// XXX: On OS X the wxSpinCtrl widget is made up of two subwidgets, unfortunatelly
// the kill focus event is not propagated to the encompassing widget,
@ -717,7 +723,7 @@ void Choice::BUILD() {
}
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// recast as a wxWindow to fit the calling convention
window = dynamic_cast<wxWindow*>(temp);
@ -1072,7 +1078,7 @@ void ColourPicker::BUILD()
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// // recast as a wxWindow to fit the calling convention
window = dynamic_cast<wxWindow*>(temp);

View file

@ -2090,20 +2090,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
contained_min_one && !m_model->objects.empty() && state != ModelInstance::PVS_Partly_Outside));
// #ys_FIXME_delete_after_testing
// bool contained = m_volumes.check_outside_state(m_config, &state);
// if (!contained)
// {
// _set_warning_texture(WarningTexture::ObjectOutside, true);
// post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside));
// }
// else
// {
// m_volumes.reset_outside_state();
// _set_warning_texture(WarningTexture::ObjectOutside, false);
// post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty()));
// }
}
else
{
@ -2887,6 +2873,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
volume_bbox.offset(1.0);
if (volume_bbox.contains(m_mouse.scene_position))
{
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
// The dragging operation is initiated.
m_mouse.drag.move_volume_idx = volume_idx;
m_selection.start_dragging();

View file

@ -105,7 +105,7 @@ static void register_dpi_event()
const auto rect = reinterpret_cast<PRECT>(lParam);
const wxRect wxrect(wxPoint(rect->top, rect->left), wxPoint(rect->bottom, rect->right));
DpiChangedEvent evt(EVT_DPI_CHANGED, dpi, wxrect);
DpiChangedEvent evt(EVT_DPI_CHANGED_SLICER, dpi, wxrect);
win->GetEventHandler()->AddPendingEvent(evt);
return true;

View file

@ -57,7 +57,7 @@ static wxBitmapComboBox* create_word_local_combo(wxWindow *parent)
#endif //__WXOSX__
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->Append(_(L("World coordinates")));
temp->Append(_(L("Local coordinates")));

View file

@ -55,7 +55,7 @@ void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback)
#endif
}
wxDEFINE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent);
wxDEFINE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent);
#ifdef _WIN32
template<class F> typename F::FN winapi_get_function(const wchar_t *dll, const char *fn_name) {

View file

@ -50,7 +50,7 @@ struct DpiChangedEvent : public wxEvent {
}
};
wxDECLARE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent);
wxDECLARE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent);
template<class P> class DPIAware : public P
{
@ -75,7 +75,7 @@ public:
// recalc_font();
this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent &evt) {
m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();

View file

@ -175,7 +175,7 @@ public:
staticbox(title!=""), extra_column(extra_clmn) {
if (staticbox) {
stb = new wxStaticBox(_parent, wxID_ANY, title);
stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
stb->SetFont(wxGetApp().bold_font());
} else
stb = nullptr;

View file

@ -403,7 +403,7 @@ const std::vector<std::string>& Preset::print_options()
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming",
"compatible_printers", "compatible_printers_condition", "inherits"
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}

View file

@ -1170,6 +1170,7 @@ void TabPrint::build()
optgroup->append_single_option_line("wipe_tower_width");
optgroup->append_single_option_line("wipe_tower_rotation_angle");
optgroup->append_single_option_line("wipe_tower_bridging");
optgroup->append_single_option_line("wipe_tower_no_sparse_layers");
optgroup->append_single_option_line("single_extruder_multi_material_priming");
optgroup = page->new_optgroup(_(L("Advanced")));