ConfigManipulation.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. // #include "libslic3r/GCodeSender.hpp"
  2. #include "ConfigManipulation.hpp"
  3. #include "I18N.hpp"
  4. #include "GUI_App.hpp"
  5. #include "format.hpp"
  6. #include "libslic3r/Model.hpp"
  7. #include "libslic3r/PresetBundle.hpp"
  8. #include <wx/msgdlg.h>
  9. namespace Slic3r {
  10. namespace GUI {
  11. void ConfigManipulation::apply(DynamicPrintConfig* config, DynamicPrintConfig* new_config)
  12. {
  13. bool modified = false;
  14. for (auto opt_key : config->diff(*new_config)) {
  15. config->set_key_value(opt_key, new_config->option(opt_key)->clone());
  16. modified = true;
  17. }
  18. if (modified && load_config != nullptr)
  19. load_config();
  20. }
  21. void ConfigManipulation::toggle_field(const std::string& opt_key, const bool toggle, int opt_index/* = -1*/)
  22. {
  23. if (local_config) {
  24. if (local_config->option(opt_key) == nullptr)
  25. return;
  26. }
  27. cb_toggle_field(opt_key, toggle, opt_index);
  28. }
  29. void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config)
  30. {
  31. // #ys_FIXME_to_delete
  32. //! Temporary workaround for the correct updates of the TextCtrl (like "layer_height"):
  33. // KillFocus() for the wxSpinCtrl use CallAfter function. So,
  34. // to except the duplicate call of the update() after dialog->ShowModal(),
  35. // let check if this process is already started.
  36. if (is_msg_dlg_already_exist)
  37. return;
  38. // layer_height shouldn't be equal to zero
  39. if (config->opt_float("layer_height") < EPSILON)
  40. {
  41. const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01."));
  42. wxMessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK);
  43. DynamicPrintConfig new_conf = *config;
  44. is_msg_dlg_already_exist = true;
  45. dialog.ShowModal();
  46. new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.01));
  47. apply(config, &new_conf);
  48. is_msg_dlg_already_exist = false;
  49. }
  50. if (fabs(config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON)
  51. {
  52. const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01."));
  53. wxMessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK);
  54. DynamicPrintConfig new_conf = *config;
  55. is_msg_dlg_already_exist = true;
  56. dialog.ShowModal();
  57. new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.01, false));
  58. apply(config, &new_conf);
  59. is_msg_dlg_already_exist = false;
  60. }
  61. double fill_density = config->option<ConfigOptionPercent>("fill_density")->value;
  62. if (config->opt_bool("spiral_vase") && !(
  63. config->opt_int("perimeters") == 1
  64. && config->opt_int("top_solid_layers") == 0
  65. && fill_density == 0
  66. && config->opt_bool("support_material") == false
  67. && config->opt_int("support_material_enforce_layers") == 0
  68. && config->opt_bool("exact_last_layer_height") == false
  69. && config->opt_bool("ensure_vertical_shell_thickness") == true
  70. && config->opt_bool("infill_dense") == false
  71. && config->opt_bool("extra_perimeters") == false
  72. && config->opt_bool("extra_perimeters_overhangs") == false
  73. && config->opt_bool("extra_perimeters_odd_layers") == false
  74. && config->opt_bool("overhangs_reverse") == false
  75. )) {
  76. wxString msg_text = _(L("The Spiral Vase mode requires:\n"
  77. "- one perimeter\n"
  78. "- no top solid layers\n"
  79. "- 0% fill density\n"
  80. "- no support material\n"
  81. "- Ensure vertical shell thickness enabled\n"
  82. "- unchecked 'exact last layer height'\n"
  83. "- unchecked 'dense infill'\n"
  84. "- unchecked 'extra perimeters'"));
  85. if (is_global_config)
  86. msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?"));
  87. wxMessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")),
  88. wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
  89. DynamicPrintConfig new_conf = *config;
  90. auto answer = dialog.ShowModal();
  91. if (!is_global_config) {
  92. if (this->local_config->get().optptr("spiral_vase"))
  93. new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false));
  94. else if (this->local_config->get().optptr("perimeters"))
  95. new_conf.set_key_value("perimeters", new ConfigOptionInt(1));
  96. else if (this->local_config->get().optptr("top_solid_layers"))
  97. new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0));
  98. else if (this->local_config->get().optptr("fill_density"))
  99. new_conf.set_key_value("fill_density", new ConfigOptionPercent(0));
  100. else if (this->local_config->get().optptr("support_material"))
  101. new_conf.set_key_value("support_material", new ConfigOptionBool(false));
  102. else if (this->local_config->get().optptr("support_material_enforce_layers"))
  103. new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0));
  104. else if (this->local_config->get().optptr("exact_last_layer_height"))
  105. new_conf.set_key_value("exact_last_layer_height", new ConfigOptionBool(false));
  106. else if (this->local_config->get().optptr("ensure_vertical_shell_thickness"))
  107. new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true));
  108. else if (this->local_config->get().optptr("infill_dense"))
  109. new_conf.set_key_value("infill_dense", new ConfigOptionBool(false));
  110. else if (this->local_config->get().optptr("extra_perimeters"))
  111. new_conf.set_key_value("extra_perimeters", new ConfigOptionBool(false));
  112. else if (this->local_config->get().optptr("extra_perimeters_overhangs"))
  113. new_conf.set_key_value("extra_perimeters_overhangs", new ConfigOptionBool(false));
  114. else if (this->local_config->get().optptr("extra_perimeters_odd_layers"))
  115. new_conf.set_key_value("extra_perimeters_odd_layers", new ConfigOptionBool(false));
  116. else if (this->local_config->get().optptr("overhangs_reverse"))
  117. new_conf.set_key_value("overhangs_reverse", new ConfigOptionBool(false));
  118. this->local_config->apply_only(new_conf, this->local_config->keys(), true);
  119. } else if (answer == wxID_YES) {
  120. new_conf.set_key_value("perimeters", new ConfigOptionInt(1));
  121. new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0));
  122. new_conf.set_key_value("fill_density", new ConfigOptionPercent(0));
  123. new_conf.set_key_value("support_material", new ConfigOptionBool(false));
  124. new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0));
  125. new_conf.set_key_value("exact_last_layer_height", new ConfigOptionBool(false));
  126. new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true));
  127. new_conf.set_key_value("infill_dense", new ConfigOptionBool(false));
  128. new_conf.set_key_value("extra_perimeters", new ConfigOptionBool(false));
  129. new_conf.set_key_value("extra_perimeters_overhangs", new ConfigOptionBool(false));
  130. new_conf.set_key_value("extra_perimeters_odd_layers", new ConfigOptionBool(false));
  131. new_conf.set_key_value("overhangs_reverse", new ConfigOptionBool(false));
  132. fill_density = 0;
  133. } else {
  134. new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false));
  135. }
  136. apply(config, &new_conf);
  137. if (cb_value_change)
  138. cb_value_change("fill_density", fill_density);
  139. }
  140. if (config->opt_bool("wipe_tower") && config->opt_bool("support_material") &&
  141. ((ConfigOptionEnumGeneric*)config->option("support_material_contact_distance_type"))->value != zdNone &&
  142. (config->opt_int("support_material_extruder") != 0 || config->opt_int("support_material_interface_extruder") != 0)) {
  143. wxString msg_text = _(L("The Wipe Tower currently supports the non-soluble supports only\n"
  144. "if they are printed with the current extruder without triggering a tool change.\n"
  145. "(both support_material_extruder and support_material_interface_extruder need to be set to 0)."));
  146. if (is_global_config)
  147. msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable the Wipe Tower?"));
  148. wxMessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")),
  149. wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
  150. DynamicPrintConfig new_conf = *config;
  151. auto answer = dialog.ShowModal();
  152. if (!is_global_config) {
  153. if (this->local_config->get().optptr("wipe_tower"))
  154. new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false));
  155. else if (this->local_config->get().optptr("support_material_extruder"))
  156. new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0));
  157. else if (this->local_config->get().optptr("support_material_interface_extruder"))
  158. new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0));
  159. else if (this->local_config->get().optptr("support_material_contact_distance_type"))
  160. new_conf.set_key_value("support_material_contact_distance_type", new ConfigOptionEnum<SupportZDistanceType>(zdNone));
  161. else if (this->local_config->get().optptr("support_material"))
  162. new_conf.set_key_value("support_material", new ConfigOptionBool(false));
  163. this->local_config->apply_only(new_conf, this->local_config->keys(), true);
  164. } else if (answer == wxID_YES) {
  165. new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0));
  166. new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0));
  167. } else
  168. new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false));
  169. apply(config, &new_conf);
  170. }
  171. if (config->opt_bool("wipe_tower") && config->opt_bool("support_material") &&
  172. ((ConfigOptionEnumGeneric*)config->option("support_material_contact_distance_type"))->value == zdNone &&
  173. !config->opt_bool("support_material_synchronize_layers")) {
  174. wxString msg_text = _(L("For the Wipe Tower to work with the soluble supports, the support layers\n"
  175. "need to be synchronized with the object layers."));
  176. if (is_global_config)
  177. msg_text += "\n\n" + _(L("Shall I synchronize support layers in order to enable the Wipe Tower?"));
  178. wxMessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")),
  179. wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
  180. DynamicPrintConfig new_conf = *config;
  181. auto answer = dialog.ShowModal();
  182. if (!is_global_config) {
  183. if (this->local_config->get().optptr("wipe_tower"))
  184. new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false));
  185. else if (this->local_config->get().optptr("support_material_synchronize_layers"))
  186. new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true));
  187. else if (this->local_config->get().optptr("support_material_contact_distance_type"))
  188. new_conf.set_key_value("support_material_contact_distance_type", new ConfigOptionEnum<SupportZDistanceType>(zdFilament));
  189. else if (this->local_config->get().optptr("support_material"))
  190. new_conf.set_key_value("support_material", new ConfigOptionBool(false));
  191. this->local_config->apply_only(new_conf, this->local_config->keys(), true);
  192. } else if (answer == wxID_YES) {
  193. new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true));
  194. } else {
  195. new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false));
  196. }
  197. apply(config, &new_conf);
  198. }
  199. // check forgotten '%'
  200. {
  201. struct optDescr {
  202. ConfigOptionFloatOrPercent* opt;
  203. std::string name;
  204. float min;
  205. float max;
  206. };
  207. float diameter = 0.4f;
  208. if (config->option<ConfigOptionFloatOrPercent>("extrusion_width")->percent) {
  209. //has to be percent
  210. diameter = 0;
  211. } else {
  212. diameter = config->option<ConfigOptionFloatOrPercent>("extrusion_width")->value;
  213. }
  214. std::vector<optDescr> opts;
  215. opts.push_back({ config->option<ConfigOptionFloatOrPercent>("infill_overlap"), "infill_overlap", 0, diameter * 10 });
  216. for (int i = 0; i < opts.size(); i++) {
  217. if ((!opts[i].opt->percent) && (opts[i].opt->get_abs_value(diameter) < opts[i].min || opts[i].opt->get_abs_value(diameter) > opts[i].max)) {
  218. wxString msg_text = _(L("Did you forgot to put a '%' in the " + opts[i].name + " field? "
  219. "it's currently set to " + std::to_string(opts[i].opt->get_abs_value(diameter)) + " mm."));
  220. if (is_global_config) {
  221. msg_text += "\n\n" + _(L("Shall I add the '%'?"));
  222. wxMessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")),
  223. wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
  224. DynamicPrintConfig new_conf = *config;
  225. auto answer = dialog.ShowModal();
  226. if (answer == wxID_YES) {
  227. new_conf.set_key_value(opts[i].name, new ConfigOptionFloatOrPercent(opts[i].opt->value * 100, true));
  228. apply(config, &new_conf);
  229. }
  230. }
  231. }
  232. }
  233. }
  234. // check changes from FloatOrPercent to percent (useful to migrate config from prusa to Slic3r)
  235. {
  236. std::vector<std::string> names;
  237. names.push_back("bridge_flow_ratio");
  238. names.push_back("over_bridge_flow_ratio");
  239. names.push_back("bridge_overlap");
  240. names.push_back("fill_top_flow_ratio");
  241. names.push_back("first_layer_flow_ratio");
  242. for (int i = 0; i < names.size(); i++) {
  243. if (config->option<ConfigOptionPercent>(names[i])->value <= 2) {
  244. DynamicPrintConfig new_conf = *config;
  245. new_conf.set_key_value(names[i], new ConfigOptionPercent(config->option<ConfigOptionPercent>(names[i])->value * 100));
  246. apply(config, &new_conf);
  247. }
  248. }
  249. }
  250. if (config->opt_float("brim_width") > 0 && config->opt_float("brim_offset") >= config->opt_float("brim_width")) {
  251. wxString msg_text = _(L("It's not possible to use a bigger value for the brim offset than the brim width, as it won't extrude anything."
  252. " Brim offset have to be lower than the brim width."));
  253. if (is_global_config) {
  254. msg_text += "\n\n" + _(L("Shall I switch the brim offset to 0?"));
  255. wxMessageDialog dialog(nullptr, msg_text, _(L("Brim configuration")),
  256. wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
  257. auto answer = dialog.ShowModal();
  258. if (!is_global_config || answer == wxID_YES) {
  259. DynamicPrintConfig new_conf = *config;
  260. new_conf.set_key_value("brim_offset", new ConfigOptionFloat(0));
  261. apply(config, &new_conf);
  262. }
  263. }
  264. }
  265. static bool support_material_overhangs_queried = false;
  266. if (config->opt_bool("support_material")) {
  267. // Ask only once.
  268. if (!support_material_overhangs_queried) {
  269. support_material_overhangs_queried = true;
  270. if (config->option<ConfigOptionFloatOrPercent>("overhangs_width_speed") == 0) {
  271. wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n"
  272. "- overhangs with bridge speed & fan"));
  273. if (is_global_config) {
  274. msg_text += "\n\n" + _(L("Shall I adjust those settings for supports?"));
  275. wxMessageDialog dialog(nullptr, msg_text, _(L("Support Generator")),
  276. wxICON_WARNING | (is_global_config ? wxYES | wxNO | wxCANCEL : wxOK));
  277. DynamicPrintConfig new_conf = *config;
  278. auto answer = dialog.ShowModal();
  279. if (!is_global_config || answer == wxID_YES) {
  280. // Enable "detect bridging perimeters".
  281. new_conf.set_key_value("overhangs_width_speed", new ConfigOptionFloatOrPercent(50, true));
  282. } else if (answer == wxID_NO) {
  283. // Do nothing, leave supports on and "detect bridging perimeters" off.
  284. } else if (answer == wxID_CANCEL) {
  285. // Disable supports.
  286. new_conf.set_key_value("support_material", new ConfigOptionBool(false));
  287. support_material_overhangs_queried = false;
  288. }
  289. apply(config, &new_conf);
  290. }
  291. }
  292. }
  293. } else {
  294. support_material_overhangs_queried = false;
  295. }
  296. if (config->option<ConfigOptionPercent>("fill_density")->value == 100) {
  297. std::string fill_pattern = config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->serialize();
  298. const std::vector<std::string>& top_fill_pattern_values = config->def()->get("top_fill_pattern")->enum_values;
  299. bool correct_100p_fill = std::find(top_fill_pattern_values.begin(), top_fill_pattern_values.end(), fill_pattern) != top_fill_pattern_values.end();
  300. if (!correct_100p_fill) {
  301. const std::vector<std::string>& bottom_fill_pattern_values = config->def()->get("bottom_fill_pattern")->enum_values;
  302. correct_100p_fill = std::find(bottom_fill_pattern_values.begin(), bottom_fill_pattern_values.end(), fill_pattern) != bottom_fill_pattern_values.end();
  303. }
  304. if (!correct_100p_fill) {
  305. // get fill_pattern name from enum_labels for using this one at dialog_msg
  306. const ConfigOptionDef* fill_pattern_def = config->def()->get("fill_pattern");
  307. assert(fill_pattern_def != nullptr);
  308. auto it_pattern = std::find(fill_pattern_def->enum_values.begin(), fill_pattern_def->enum_values.end(), fill_pattern);
  309. assert(it_pattern != fill_pattern_def->enum_values.end());
  310. if (it_pattern != fill_pattern_def->enum_values.end()) {
  311. wxString msg_text = GUI::format_wxstr(_L("The %1% infill pattern is not supposed to work at 100%% density."),
  312. _(fill_pattern_def->enum_labels[it_pattern - fill_pattern_def->enum_values.begin()]));
  313. if (is_global_config) {
  314. msg_text += "\n\n" + _L("Shall I switch to rectilinear fill pattern?");
  315. wxMessageDialog dialog(nullptr, msg_text, _L("Infill"),
  316. wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
  317. DynamicPrintConfig new_conf = *config;
  318. auto answer = dialog.ShowModal();
  319. if (!is_global_config || answer == wxID_YES) {
  320. new_conf.set_key_value("fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear));
  321. fill_density = 100;
  322. } else
  323. fill_density = wxGetApp().preset_bundle->fff_prints.get_selected_preset().config.option<ConfigOptionPercent>("fill_density")->value;
  324. new_conf.set_key_value("fill_density", new ConfigOptionPercent(fill_density));
  325. apply(config, &new_conf);
  326. if (cb_value_change)
  327. cb_value_change("fill_density", fill_density);
  328. }
  329. }
  330. }
  331. }
  332. }
  333. void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
  334. {
  335. bool have_perimeters = config->opt_int("perimeters") > 0;
  336. for (auto el : { "ensure_vertical_shell_thickness", "external_perimeter_speed", "extra_perimeters", "extra_perimeters_overhangs", "extra_perimeters_odd_layers",
  337. "external_perimeters_first", "external_perimeters_vase", "external_perimeter_extrusion_width",
  338. "no_perimeter_unsupported_algo", "only_one_perimeter_top", "overhangs", "overhangs_reverse",
  339. "perimeter_loop", "perimeter_loop_seam","perimeter_speed",
  340. "seam_position", "small_perimeter_speed", "small_perimeter_min_length", " small_perimeter_max_length", "spiral_vase",
  341. "thin_walls", "thin_perimeters"})
  342. toggle_field(el, have_perimeters);
  343. toggle_field("overhangs_width", config->option<ConfigOptionFloatOrPercent>("overhangs_width_speed")->value > 0);
  344. toggle_field("overhangs_reverse_threshold", have_perimeters && config->opt_bool("overhangs_reverse"));
  345. toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top"));
  346. toggle_field("thin_perimeters_all", have_perimeters && config->opt_bool("thin_perimeters"));
  347. for (auto el : { "external_perimeters_vase", "external_perimeters_nothole", "external_perimeters_hole", "perimeter_bonding"})
  348. toggle_field(el, config->opt_bool("external_perimeters_first"));
  349. for (auto el : { "thin_walls_min_width", "thin_walls_overlap", "thin_walls_merge" })
  350. toggle_field(el, have_perimeters && config->opt_bool("thin_walls"));
  351. for (auto el : { "seam_angle_cost", "seam_travel_cost" })
  352. toggle_field(el, have_perimeters && config->option<ConfigOptionEnum<SeamPosition>>("seam_position")->value == SeamPosition::spCost);
  353. toggle_field("perimeter_loop_seam", config->opt_bool("perimeter_loop"));
  354. for (auto el : { "gap_fill_last", "gap_fill_min_area" })
  355. toggle_field(el, config->opt_bool("gap_fill"));
  356. toggle_field("avoid_crossing_not_first_layer", config->opt_bool("avoid_crossing_perimeters"));
  357. bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
  358. // infill_extruder uses the same logic as in Print::extruders()
  359. for (auto el : { "fill_pattern", "infill_connection", "infill_every_layers", "infill_only_where_needed",
  360. "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder", "infill_anchor_max" })
  361. toggle_field(el, have_infill);
  362. // Only allow configuration of open anchors if the anchoring is enabled.
  363. bool has_infill_anchors = have_infill && config->option<ConfigOptionEnum<InfillConnection>>("infill_connection")->value != InfillConnection::icNotConnected;
  364. toggle_field("infill_anchor_max", has_infill_anchors);
  365. has_infill_anchors = has_infill_anchors && config->option<ConfigOptionFloatOrPercent>("infill_anchor_max")->value > 0;
  366. toggle_field("infill_anchor", has_infill_anchors);
  367. bool can_have_infill_dense = config->option<ConfigOptionPercent>("fill_density")->value < 50;
  368. for (auto el : { "infill_dense" })
  369. toggle_field(el, can_have_infill_dense);
  370. bool have_infill_dense = config->opt_bool("infill_dense") && can_have_infill_dense;
  371. for (auto el : { "infill_dense_algo" })
  372. toggle_field(el, have_infill_dense);
  373. bool has_spiral_vase = have_perimeters && config->opt_bool("spiral_vase");
  374. bool has_top_solid_infill = config->opt_int("top_solid_layers") > 0 || has_spiral_vase;
  375. bool has_bottom_solid_infill = config->opt_int("bottom_solid_layers") > 0;
  376. bool has_solid_infill = has_top_solid_infill || has_bottom_solid_infill || (have_infill && (config->opt_int("solid_infill_every_layers") > 0 || config->opt_float("solid_infill_below_area") > 0));
  377. // solid_infill_extruder uses the same logic as in Print::extruders()
  378. for (auto el : { "top_fill_pattern", "bottom_fill_pattern", "solid_fill_pattern", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin",
  379. "infill_first", "solid_infill_extruder", "solid_infill_extrusion_width", "solid_infill_speed" })
  380. toggle_field(el, has_solid_infill);
  381. for (auto el : { "fill_angle", "fill_angle_increment", "bridge_angle", "infill_extrusion_width",
  382. "infill_speed" })
  383. toggle_field(el, have_infill || has_solid_infill);
  384. toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill);
  385. toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill);
  386. // gap fill can appear in infill
  387. //toggle_field("gap_fill_speed", have_perimeters && config->opt_bool("gap_fill"));
  388. bool has_ironing_pattern = config->opt_enum<InfillPattern>("top_fill_pattern") == InfillPattern::ipSmooth
  389. || config->opt_enum<InfillPattern>("bottom_fill_pattern") == InfillPattern::ipSmooth
  390. || config->opt_enum<InfillPattern>("solid_fill_pattern") == InfillPattern::ipSmooth;
  391. for (auto el : {"fill_smooth_width, fill_smooth_distribution" })
  392. toggle_field(el, has_ironing_pattern);
  393. for (auto el : { "ironing", "top_fill_pattern", "infill_connection_top", "top_infill_extrusion_width", "top_solid_infill_speed" })
  394. toggle_field(el, has_top_solid_infill);
  395. for (auto el : { "bottom_fill_pattern", "infill_connection_bottom" })
  396. toggle_field(el, has_bottom_solid_infill);
  397. for (auto el : { "solid_fill_pattern", "infill_connection_solid" })
  398. toggle_field(el, has_solid_infill); // should be top_solid_layers") > 1 || bottom_solid_layers") > 1
  399. for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" })
  400. toggle_field(el, config->opt_bool("hole_to_polyhole"));
  401. bool have_default_acceleration = config->option<ConfigOptionFloatOrPercent>("default_acceleration")->value > 0;
  402. for (auto el : { "perimeter_acceleration", "infill_acceleration",
  403. "bridge_acceleration", "first_layer_acceleration", "travel_acceleration" })
  404. toggle_field(el, have_default_acceleration);
  405. bool have_skirt = config->opt_int("skirts") > 0;
  406. toggle_field("skirt_height", have_skirt && !config->opt_bool("draft_shield"));
  407. toggle_field("skirt_width", have_skirt);
  408. for (auto el : { "skirt_brim", "skirt_distance", "skirt_distance_from_brim", "draft_shield", "min_skirt_length" })
  409. toggle_field(el, have_skirt);
  410. bool have_brim = config->opt_float("brim_width") > 0 || config->opt_float("brim_width_interior") > 0;
  411. // perimeter_extruder uses the same logic as in Print::extruders()
  412. toggle_field("perimeter_extruder", have_perimeters || have_brim);
  413. toggle_field("brim_ears", config->opt_float("brim_width") > 0);
  414. toggle_field("brim_inside_holes", config->opt_float("brim_width") > 0 && config->opt_float("brim_width_interior") == 0);
  415. toggle_field("brim_ears_max_angle", have_brim && config->opt_bool("brim_ears"));
  416. toggle_field("brim_ears_pattern", have_brim && config->opt_bool("brim_ears"));
  417. bool have_raft = config->opt_int("raft_layers") > 0;
  418. bool have_support_material = config->opt_bool("support_material") || have_raft;
  419. bool have_support_material_auto = have_support_material && config->opt_bool("support_material_auto");
  420. bool have_support_interface = config->opt_int("support_material_interface_layers") > 0;
  421. bool have_support_soluble = have_support_material && ((ConfigOptionEnumGeneric*)config->option("support_material_contact_distance_type"))->value == zdNone;
  422. for (auto el : { "support_material_pattern", "support_material_with_sheath",
  423. "support_material_spacing", "support_material_angle", "support_material_interface_layers",
  424. "dont_support_bridges", "support_material_extrusion_width",
  425. "support_material_contact_distance_type",
  426. "support_material_xy_spacing", "support_material_interface_pattern" })
  427. toggle_field(el, have_support_material);
  428. toggle_field("support_material_threshold", have_support_material_auto);
  429. for (auto el : { "support_material_contact_distance_top",
  430. "support_material_contact_distance_bottom" })
  431. toggle_field(el, have_support_material && !have_support_soluble);
  432. for (auto el : { "support_material_interface_spacing", "support_material_interface_extruder",
  433. "support_material_interface_speed", "support_material_interface_contact_loops" })
  434. toggle_field(el, have_support_material && have_support_interface);
  435. toggle_field("support_material_synchronize_layers", have_support_soluble);
  436. toggle_field("perimeter_extrusion_width", have_perimeters || have_skirt || have_brim);
  437. toggle_field("support_material_extruder", have_support_material || have_skirt);
  438. toggle_field("support_material_speed", have_support_material || have_brim || have_skirt);
  439. bool has_PP_ironing = has_top_solid_infill && config->opt_bool("ironing");
  440. for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_angle" })
  441. toggle_field(el, has_PP_ironing);
  442. bool has_ironing = has_PP_ironing || has_ironing_pattern;
  443. for (auto el : { "ironing_speed" })
  444. toggle_field(el, has_ironing);
  445. bool have_sequential_printing = config->opt_bool("complete_objects");
  446. for (auto el : { /*"extruder_clearance_radius", "extruder_clearance_height",*/ "complete_objects_one_skirt",
  447. "complete_objects_sort", "complete_objects_one_brim"})
  448. toggle_field(el, have_sequential_printing);
  449. bool have_ooze_prevention = config->opt_bool("ooze_prevention");
  450. toggle_field("standby_temperature_delta", have_ooze_prevention);
  451. bool have_wipe_tower = config->opt_bool("wipe_tower");
  452. for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
  453. "wipe_tower_bridging", "wipe_tower_brim", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" })
  454. toggle_field(el, have_wipe_tower);
  455. bool have_avoid_crossing_perimeters = config->opt_bool("avoid_crossing_perimeters");
  456. toggle_field("avoid_crossing_perimeters_max_detour", have_avoid_crossing_perimeters);
  457. for (auto el : { "fill_smooth_width", "fill_smooth_distribution" })
  458. toggle_field(el, (has_top_solid_infill && config->option<ConfigOptionEnum<InfillPattern>>("top_fill_pattern")->value == InfillPattern::ipSmooth)
  459. || (has_bottom_solid_infill && config->option<ConfigOptionEnum<InfillPattern>>("bottom_fill_pattern")->value == InfillPattern::ipSmooth)
  460. || (has_solid_infill && config->option<ConfigOptionEnum<InfillPattern>>("solid_fill_pattern")->value == InfillPattern::ipSmooth)
  461. || (have_support_material && config->option<ConfigOptionEnum<InfillPattern>>("support_material_interface_pattern")->value == InfillPattern::ipSmooth));
  462. //TODO: can the milling_diameter or the milling_cutter be check to enable/disable this?
  463. for (auto el : { "milling_after_z", "milling_extra_size", "milling_speed" })
  464. toggle_field(el, config->opt_bool("milling_post_process"));
  465. }
  466. void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
  467. {
  468. double head_penetration = config->opt_float("support_head_penetration");
  469. double head_width = config->opt_float("support_head_width");
  470. if (head_penetration > head_width) {
  471. wxString msg_text = _(L("Head penetration should not be greater than the head width."));
  472. wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK);
  473. DynamicPrintConfig new_conf = *config;
  474. if (dialog.ShowModal() == wxID_OK) {
  475. new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width));
  476. apply(config, &new_conf);
  477. }
  478. }
  479. double pinhead_d = config->opt_float("support_head_front_diameter");
  480. double pillar_d = config->opt_float("support_pillar_diameter");
  481. if (pinhead_d > pillar_d) {
  482. wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter."));
  483. wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK);
  484. DynamicPrintConfig new_conf = *config;
  485. if (dialog.ShowModal() == wxID_OK) {
  486. new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0));
  487. apply(config, &new_conf);
  488. }
  489. }
  490. }
  491. void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)
  492. {
  493. bool supports_en = config->opt_bool("supports_enable");
  494. toggle_field("support_head_front_diameter", supports_en);
  495. toggle_field("support_head_penetration", supports_en);
  496. toggle_field("support_head_width", supports_en);
  497. toggle_field("support_pillar_diameter", supports_en);
  498. toggle_field("support_small_pillar_diameter_percent", supports_en);
  499. toggle_field("support_max_bridges_on_pillar", supports_en);
  500. toggle_field("support_pillar_connection_mode", supports_en);
  501. toggle_field("support_buildplate_only", supports_en);
  502. toggle_field("support_base_diameter", supports_en);
  503. toggle_field("support_base_height", supports_en);
  504. toggle_field("support_base_safety_distance", supports_en);
  505. toggle_field("support_critical_angle", supports_en);
  506. toggle_field("support_max_bridge_length", supports_en);
  507. toggle_field("support_max_pillar_link_distance", supports_en);
  508. toggle_field("support_points_density_relative", supports_en);
  509. toggle_field("support_points_minimal_distance", supports_en);
  510. bool pad_en = config->opt_bool("pad_enable");
  511. toggle_field("pad_wall_thickness", pad_en);
  512. toggle_field("pad_wall_height", pad_en);
  513. toggle_field("pad_brim_size", pad_en);
  514. toggle_field("pad_max_merge_distance", pad_en);
  515. // toggle_field("pad_edge_radius", supports_en);
  516. toggle_field("pad_wall_slope", pad_en);
  517. toggle_field("pad_around_object", pad_en);
  518. toggle_field("pad_around_object_everywhere", pad_en);
  519. bool zero_elev = config->opt_bool("pad_around_object") && pad_en;
  520. toggle_field("support_object_elevation", supports_en && !zero_elev);
  521. toggle_field("pad_object_gap", zero_elev);
  522. toggle_field("pad_around_object_everywhere", zero_elev);
  523. toggle_field("pad_object_connector_stride", zero_elev);
  524. toggle_field("pad_object_connector_width", zero_elev);
  525. toggle_field("pad_object_connector_penetration", zero_elev);
  526. }
  527. } // GUI
  528. } // Slic3r