diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index a030f57ca..759030ec5 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -94,7 +94,8 @@ sub OnInit { Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION; $self->{notifier} = Slic3r::GUI::Notifier->new; - + $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new; + # locate or create data directory # Unix: ~/.Slic3r # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index db1521499..b0897ab66 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -21,7 +21,7 @@ use File::Basename qw(basename); use List::Util qw(first); use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :window :button wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED); +use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_CHECKBOX EVT_TREE_SEL_CHANGED); use base qw(Wx::Panel Class::Accessor); # Index of the currently active preset. @@ -522,7 +522,7 @@ package Slic3r::GUI::Tab::Print; use base 'Slic3r::GUI::Tab'; use List::Util qw(first); -use Wx qw(:icon :dialog :id); +use Wx qw(:icon :dialog :id wxTheApp); sub name { 'print' } sub title { 'Print Settings' } @@ -530,49 +530,7 @@ sub title { 'Print Settings' } sub build { my $self = shift; - $self->init_config_options(qw( - layer_height first_layer_height - perimeters spiral_vase - top_solid_layers bottom_solid_layers - extra_perimeters ensure_vertical_shell_thickness avoid_crossing_perimeters thin_walls overhangs - seam_position external_perimeters_first - fill_density fill_pattern external_fill_pattern - infill_every_layers infill_only_where_needed - solid_infill_every_layers fill_angle bridge_angle solid_infill_below_area - only_retract_when_crossing_perimeters infill_first - max_print_speed max_volumetric_speed - max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative - perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed - solid_infill_speed top_solid_infill_speed support_material_speed - support_material_xy_spacing - support_material_interface_speed bridge_speed gap_fill_speed - travel_speed - first_layer_speed - perimeter_acceleration infill_acceleration bridge_acceleration - first_layer_acceleration default_acceleration - skirts skirt_distance skirt_height min_skirt_length - brim_width - support_material support_material_threshold support_material_enforce_layers - raft_layers - support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle - support_material_interface_layers support_material_interface_spacing support_material_interface_contact_loops - support_material_contact_distance support_material_buildplate_only dont_support_bridges - notes - complete_objects extruder_clearance_radius extruder_clearance_height - gcode_comments output_filename_format - post_process - perimeter_extruder infill_extruder solid_infill_extruder - support_material_extruder support_material_interface_extruder - ooze_prevention standby_temperature_delta - interface_shells - extrusion_width first_layer_extrusion_width perimeter_extrusion_width - external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width - 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_per_color_wipe - )); - $self->{config}->set('print_settings_id', ''); + $self->{config}->apply(wxTheApp->{preset_bundle}->prints->preset(0)->config); { my $page = $self->add_options_page('Layers and perimeters', 'layers.png'); @@ -776,7 +734,7 @@ sub build { $optgroup->append_single_option_line('clip_multipart_objects'); $optgroup->append_single_option_line('elefant_foot_compensation'); $optgroup->append_single_option_line('xy_size_compensation'); - $optgroup->append_single_option_line('threads') if $Slic3r::have_threads; +# $optgroup->append_single_option_line('threads') if $Slic3r::have_threads; $optgroup->append_single_option_line('resolution'); } } @@ -831,6 +789,12 @@ sub build { } } +sub reload_config { + my ($self) = @_; +# $self->_reload_compatible_printers_widget; + $self->SUPER::reload_config; +} + # Slic3r::GUI::Tab::Print::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing. @@ -1035,10 +999,11 @@ sub _update { for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe); } -sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () } +#sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () } package Slic3r::GUI::Tab::Filament; use base 'Slic3r::GUI::Tab'; +use Wx qw(wxTheApp); sub name { 'filament' } sub title { 'Filament Settings' } @@ -1046,15 +1011,7 @@ sub title { 'Filament Settings' } sub build { my $self = shift; - $self->init_config_options(qw( - filament_colour filament_diameter filament_type filament_soluble filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost - temperature first_layer_temperature bed_temperature first_layer_bed_temperature - fan_always_on cooling - min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers - fan_below_layer_time slowdown_below_layer_time min_print_speed - start_filament_gcode end_filament_gcode - )); - $self->{config}->set('filament_settings_id', ''); + $self->{config}->apply(wxTheApp->{preset_bundle}->filaments->preset(0)->config); { my $page = $self->add_options_page('Filament', 'spool.png'); @@ -1236,20 +1193,7 @@ sub build { my $self = shift; my (%params) = @_; - $self->init_config_options(qw( - bed_shape z_offset - gcode_flavor use_relative_e_distances - serial_port serial_speed - octoprint_host octoprint_apikey - use_firmware_retraction - use_volumetric_e variable_layer_height - single_extruder_multi_material start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode - nozzle_diameter extruder_offset - retract_length retract_lift retract_speed deretract_speed retract_before_wipe retract_restart_extra retract_before_travel retract_layer_change wipe - retract_length_toolchange retract_restart_extra_toolchange - extruder_colour printer_notes - )); - $self->{config}->set('printer_settings_id', ''); + $self->{config}->apply(wxTheApp->{preset_bundle}->printers->preset(0)->config); my $bed_shape_widget = sub { my ($parent) = @_; diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index aa2ca0e51..e761d388b 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -28,6 +28,8 @@ if(WIN32) add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB) endif() +add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE) + add_library(libslic3r STATIC ${LIBDIR}/libslic3r/BoundingBox.cpp ${LIBDIR}/libslic3r/BoundingBox.hpp @@ -164,6 +166,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/3DScene.hpp ${LIBDIR}/slic3r/GUI/GLShader.cpp ${LIBDIR}/slic3r/GUI/GLShader.hpp + ${LIBDIR}/slic3r/GUI/Preset.cpp + ${LIBDIR}/slic3r/GUI/Preset.hpp ${LIBDIR}/slic3r/GUI/GUI.cpp ${LIBDIR}/slic3r/GUI/GUI.hpp ) @@ -276,6 +280,7 @@ set(XS_XSP_FILES ${XSP_DIR}/Geometry.xsp ${XSP_DIR}/GUI.xsp ${XSP_DIR}/GUI_3DScene.xsp + ${XSP_DIR}/GUI_Preset.xsp ${XSP_DIR}/Layer.xsp ${XSP_DIR}/Line.xsp ${XSP_DIR}/Model.xsp @@ -460,13 +465,13 @@ if (SLIC3R_PRUSACONTROL) set(wxWidgets_UseAlienWx 1) if (wxWidgets_UseAlienWx) set(AlienWx_DEBUG 1) - find_package(AlienWx REQUIRED COMPONENTS base) + find_package(AlienWx REQUIRED COMPONENTS base core adv) include_directories(${AlienWx_INCLUDE_DIRS}) #add_compile_options(${AlienWx_CXX_FLAGS}) add_definitions(${AlienWx_DEFINITIONS}) set(wxWidgets_LIBRARIES ${AlienWx_LIBRARIES}) else () - find_package(wxWidgets REQUIRED COMPONENTS base) + find_package(wxWidgets REQUIRED COMPONENTS base core adv) include(${wxWidgets_USE_FILE}) endif () add_definitions(-DSLIC3R_GUI -DSLIC3R_PRUS) diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index e72890e99..b79abac7c 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -282,6 +282,8 @@ for my $class (qw( Slic3r::Geometry::BoundingBoxf Slic3r::Geometry::BoundingBoxf3 Slic3r::GUI::_3DScene::GLVolume + Slic3r::GUI::Preset + Slic3r::GUI::PresetCollection Slic3r::Layer Slic3r::Layer::Region Slic3r::Layer::Support diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index ffb315704..1e548c8fc 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -49,7 +49,7 @@ public: void set(const ConfigOption &option) { const ConfigOptionSingle* other = dynamic_cast< const ConfigOptionSingle* >(&option); - if (other != NULL) this->value = other->value; + if (other != nullptr) this->value = other->value; }; }; @@ -71,13 +71,15 @@ public: void set(const ConfigOption &option) { const ConfigOptionVector* other = dynamic_cast< const ConfigOptionVector* >(&option); - if (other != NULL) this->values = other->values; + if (other != nullptr) this->values = other->values; }; - T get_at(size_t i) const { + T& get_at(size_t i) { assert(! this->values.empty()); return (i < this->values.size()) ? this->values[i] : this->values.front(); }; + + const T& get_at(size_t i) const { return const_cast*>(this)->get_at(i); } }; class ConfigOptionFloat : public ConfigOptionSingle @@ -434,12 +436,25 @@ public: }; }; -class ConfigOptionBools : public ConfigOptionVector +class ConfigOptionBools : public ConfigOptionVector { -public: +public: + void set(const ConfigOption &option) { + const ConfigOptionVector* other = dynamic_cast*>(&option); + if (other != nullptr) + this->values = other->values; + }; + + bool& get_at(size_t i) { + assert(! this->values.empty()); + return *reinterpret_cast(&((i < this->values.size()) ? this->values[i] : this->values.front())); + }; + + bool get_at(size_t i) const { return bool((i < this->values.size()) ? this->values[i] : this->values.front()); } + std::string serialize() const { std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ","; ss << (*it ? "1" : "0"); } @@ -448,7 +463,7 @@ public: std::vector vserialize() const { std::vector vv; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { std::ostringstream ss; ss << (*it ? "1" : "0"); vv.push_back(ss.str()); @@ -702,6 +717,16 @@ public: t_config_option_keys keys() const; void erase(const t_config_option_key &opt_key) { this->options.erase(opt_key); } + std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return dynamic_cast(this->optptr(opt_key, create))->value; } + const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast(this)->opt_string(opt_key); } + std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast(this->optptr(opt_key))->get_at(idx); } + const std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) const { return const_cast(this)->opt_string(opt_key, idx); } + + double& opt_float(const t_config_option_key &opt_key) { return dynamic_cast(this->optptr(opt_key))->value; } + const double& opt_float(const t_config_option_key &opt_key) const { return const_cast(this)->opt_float(opt_key); } + double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast(this->optptr(opt_key))->get_at(idx); } + const double& opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return const_cast(this)->opt_float(opt_key, idx); } + private: typedef std::map t_options_map; t_options_map options; diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 17c343589..3b7a62299 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -57,6 +57,9 @@ REGISTER_CLASS(TriangleMesh, "TriangleMesh"); REGISTER_CLASS(GLShader, "GUI::_3DScene::GLShader"); REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume"); REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection"); +REGISTER_CLASS(Preset, "GUI::Preset"); +REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); +REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); SV* ConfigBase__as_hash(ConfigBase* THIS) { @@ -138,8 +141,8 @@ ConfigOption_to_SV(const ConfigOption &opt, const ConfigOptionDef &def) { const ConfigOptionBools* optv = dynamic_cast(&opt); AV* av = newAV(); av_fill(av, optv->values.size()-1); - for (std::vector::const_iterator it = optv->values.begin(); it != optv->values.end(); ++it) - av_store(av, it - optv->values.begin(), newSViv(*it ? 1 : 0)); + for (size_t i = 0; i < optv->values.size(); ++ i) + av_store(av, i, newSViv(optv->values[i] ? 1 : 0)); return newRV_noinc((SV*)av); } else { std::string serialized = opt.serialize(); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 18e6d6c6e..24bb01dc5 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -704,7 +704,7 @@ void _3DScene::_glew_init() glewInit(); } -inline int hex_digit_to_int(const char c) +static inline int hex_digit_to_int(const char c) { return (c >= '0' && c <= '9') ? int(c - '0') : @@ -712,7 +712,7 @@ inline int hex_digit_to_int(const char c) (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; } -inline std::vector parse_colors(const std::vector &scolors) +static inline std::vector parse_colors(const std::vector &scolors) { std::vector output(scolors.size() * 4, 1.f); for (size_t i = 0; i < scolors.size(); ++ i) { diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp new file mode 100644 index 000000000..e9487ab61 --- /dev/null +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -0,0 +1,323 @@ +#include "Preset.hpp" + +#include +#include + +#include +#include + +namespace Slic3r { + +// Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. +// In case of a "default" config item, return the default values. +DynamicPrintConfig& Preset::load(const std::vector &keys) +{ + // Set the configuration from the defaults. + Slic3r::FullPrintConfig defaults; + this->config.apply(defaults, keys.empty() ? defaults.keys() : keys); + + if (! this->is_default) { + // Load the preset file, apply preset values on top of defaults. + try { + if (boost::algorithm::iends_with(this->file, ".gcode") || boost::algorithm::iends_with(this->file, ".g")) + this->config.load_from_gcode(this->file); + else + this->config.load(this->file); + } catch (const std::ifstream::failure&) { + throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + this->file); + } catch (const std::runtime_error&) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file); + } + + if (this->type == TYPE_PRINTER && std::find(keys.begin(), keys.end(), "nozzle_diameter") != keys.end()) { + // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. + auto *nozzle_diameter = dynamic_cast(this->config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + auto *deretract_speed = dynamic_cast(this->config.option("deretract_speed")); + deretract_speed->values.resize(num_extruders, deretract_speed->values.empty() ? + defaults.deretract_speed.values.front() : deretract_speed->values.front()); + auto *extruder_colour = dynamic_cast(this->config.option("extruder_colour")); + extruder_colour->values.resize(num_extruders, extruder_colour->values.empty() ? + defaults.extruder_colour.values.front() : extruder_colour->values.front()); + auto *retract_before_wipe = dynamic_cast(this->config.option("retract_before_wipe")); + retract_before_wipe->values.resize(num_extruders, retract_before_wipe->values.empty() ? + defaults.retract_before_wipe.values.front() : retract_before_wipe->values.front()); + } + } + + this->loaded = true; + return this->config; +} + +bool Preset::enable_compatible(const std::string &active_printer) +{ + auto *compatible_printers = dynamic_cast(this->config.optptr("compatible_printers")); + this->is_visible = compatible_printers && ! compatible_printers->values.empty() && + std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer) != + compatible_printers->values.end(); + return this->is_visible; +} + +PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys) : + m_type(type), + m_edited_preset(type, "", false) +{ + // Insert just the default preset. + m_presets.emplace_back(Preset(type, "- default -", true)); + m_presets.front().load(keys); +} + +void PresetCollection::set_default_suppressed(bool default_suppressed) +{ + if (m_default_suppressed != default_suppressed) { + m_default_suppressed = default_suppressed; + m_presets.front().is_visible = ! default_suppressed || m_presets.size() > 1; + } +} + +void PresetCollection::enable_disable_compatible_to_printer(const std::string &active_printer) +{ + size_t num_visible = 0; + for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) + if (m_presets[idx_preset].enable_compatible(active_printer)) + ++ num_visible; + if (num_visible == 0) + // Show the "-- default --" preset. + m_presets.front().is_visible = true; +} + +static std::string g_suffix_modified = " (modified)"; + +// Update the wxChoice UI component from this list of presets. +// Hide the +void PresetCollection::update_editor_ui(wxBitmapComboBox *ui) +{ + if (ui == nullptr) + return; + + size_t n_visible = this->num_visible(); + size_t n_choice = size_t(ui->GetCount()); + std::string name_selected = ui->GetStringSelection().ToUTF8(); + if (boost::algorithm::iends_with(name_selected, g_suffix_modified)) + // Remove the g_suffix_modified. + name_selected.erase(name_selected.end() - g_suffix_modified.size(), name_selected.end()); +#if 0 + if (std::abs(int(n_visible) - int(n_choice)) <= 1) { + // The number of items differs by at most one, update the choice. + size_t i_preset = 0; + size_t i_ui = 0; + while (i_preset < presets.size()) { + std::string name_ui = ui->GetString(i_ui).ToUTF8(); + if (boost::algorithm::iends_with(name_ui, g_suffix_modified)) + // Remove the g_suffix_modified. + name_ui.erase(name_ui.end() - g_suffix_modified.size(), name_ui.end()); + while (this->presets[i_preset].name ) + const Preset &preset = this->presets[i_preset]; + if (preset) + } + } else +#endif + { + // Otherwise fill in the list from scratch. + ui->Clear(); + for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { + const Preset &preset = this->m_presets[i]; + const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? wxNullBitmap : *bmp, (void*)i); + if (name_selected == preset.name) + ui->SetSelection(ui->GetCount() - 1); + } + } +} + +void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) +{ + if (ui == nullptr) + return; + + size_t n_visible = this->num_visible(); + size_t n_choice = size_t(ui->GetCount()); + if (std::abs(int(n_visible) - int(n_choice)) <= 1) { + // The number of items differs by at most one, update the choice. + } else { + // Otherwise fill in the list from scratch. + } +} + +PresetBundle::PresetBundle() : + prints(Preset::TYPE_PRINT, print_options()), + filaments(Preset::TYPE_FILAMENT, filament_options()), + printers(Preset::TYPE_PRINTER, printer_options()), + m_bitmapCompatible(new wxBitmap), + m_bitmapIncompatible(new wxBitmap) +{ + // Create the ID config keys, as they are not part of the Static print config classes. + this->prints.preset(0).config.opt_string("print_settings_id", true); + this->filaments.preset(0).config.opt_string("filament_settings_id", true); + this->printers.preset(0).config.opt_string("print_settings_id", true); + // Create the "compatible printers" keys, as they are not part of the Static print config classes. + this->filaments.preset(0).config.optptr("compatible_printers", true); + this->prints.preset(0).config.optptr("compatible_printers", true); +} + +PresetBundle::~PresetBundle() +{ + delete m_bitmapCompatible; + delete m_bitmapIncompatible; + for (std::pair &bitmap : m_mapColorToBitmap) + delete bitmap.second; +} + +bool PresetBundle::load_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) +{ + bool loaded_compatible = m_bitmapCompatible ->LoadFile(wxString::FromUTF8(path_bitmap_compatible.c_str())); + bool loaded_incompatible = m_bitmapIncompatible->LoadFile(wxString::FromUTF8(path_bitmap_incompatible.c_str())); + if (loaded_compatible) { + prints .set_bitmap_compatible(m_bitmapCompatible); + filaments.set_bitmap_compatible(m_bitmapCompatible); + printers .set_bitmap_compatible(m_bitmapCompatible); + } + if (loaded_incompatible) { + prints .set_bitmap_compatible(m_bitmapIncompatible); + filaments.set_bitmap_compatible(m_bitmapIncompatible); + printers .set_bitmap_compatible(m_bitmapIncompatible); + } + return loaded_compatible && loaded_incompatible; +} + +static inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} + +static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out) +{ + rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; + const char *c = scolor.data() + 1; + if (scolor.size() != 7 || scolor.front() != '#') + return false; + for (size_t i = 0; i < 3; ++ i) { + int digit1 = hex_digit_to_int(*c ++); + int digit2 = hex_digit_to_int(*c ++); + if (digit1 == -1 || digit2 == -1) + return false; + rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); + } + return true; +} + +// Update the colors preview at the platter extruder combo box. +void PresetBundle::update_platter_filament_ui_colors(wxBitmapComboBox *ui, unsigned int idx_extruder, unsigned int idx_filament) +{ + unsigned char rgb[3]; + std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); + if (! parse_color(extruder_color, rgb)) + // Extruder color is not defined. + extruder_color.clear(); + + for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { + if (! ui->HasClientUntypedData()) + continue; + size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); + const Preset &filament_preset = filaments.preset(filament_preset_id); + // Assign an extruder color to the selected item if the extruder color is defined. + std::string filament_rgb = filament_preset.config.opt_string("filament_colour", 0); + std::string extruder_rgb = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; + wxBitmap *bitmap = nullptr; + if (filament_rgb == extruder_rgb) { + auto it = m_mapColorToBitmap.find(filament_rgb); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + parse_color(filament_rgb, rgb); + wxImage image(24, 16); + image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); + } else { + bitmap = it->second; + } + } else { + std::string bitmap_key = filament_rgb + extruder_rgb; + auto it = m_mapColorToBitmap.find(bitmap_key); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + wxImage image(24, 16); + parse_color(extruder_rgb, rgb); + image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); + parse_color(filament_rgb, rgb); + image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); + } else { + bitmap = it->second; + } + } + ui->SetItemBitmap(ui_id, *bitmap); + } +} + +const std::vector& PresetBundle::print_options() +{ + const char *opts[] = { + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", + "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", + "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", + "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", + "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", + "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", + "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", + "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", + "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", + "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", + "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", + "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", + "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", + "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", + "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", + "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", + "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_per_color_wipe" + }; + static std::vector s_opts; + if (s_opts.empty()) + s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); + return s_opts; +} + +const std::vector& PresetBundle::filament_options() +{ + const char *opts[] = { + "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", + "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", + "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", + "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", + "end_filament_gcode" + }; + static std::vector s_opts; + if (s_opts.empty()) + s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); + return s_opts; +} + +const std::vector& PresetBundle::printer_options() +{ + const char *opts[] = { + "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", + "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", + "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed", + "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe", + "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" + }; + static std::vector s_opts; + if (s_opts.empty()) + s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); + return s_opts; +} + +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp new file mode 100644 index 000000000..c01c5a8fb --- /dev/null +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -0,0 +1,146 @@ +#ifndef slic3r_Preset_hpp_ +#define slic3r_Preset_hpp_ + +#include "../../libslic3r/libslic3r.h" +#include "../../libslic3r/PrintConfig.hpp" + +class wxBitmap; +class wxBitmapComboBox; + +namespace Slic3r { + +class Preset +{ +public: + enum Type + { + TYPE_INVALID, + TYPE_PRINT, + TYPE_FILAMENT, + TYPE_PRINTER, + }; + + Preset(Type type, const std::string &name, bool is_default = false) : type(type), name(name), is_default(is_default) {} + + Type type = TYPE_INVALID; + + // The preset represents a "default" set of properties, + // pulled from the default values of the PrintConfig (see PrintConfigDef for their definitions). + bool is_default; + // External preset points to a configuration, which has been loaded but not imported + // into the Slic3r default configuration location. + bool is_external = false; + // Preset is visible, if it is compatible with the active Printer. + // Also the "default" preset is only visible, if it is the only preset in the list. + bool is_visible = true; + // Has this preset been modified? + bool is_dirty = false; + + // Name of the preset, usually derived form the file name. + std::string name; + // File name of the preset. This could be a Print / Filament / Printer preset, + // or a Configuration file bundling the Print + Filament + Printer presets (in that case is_external will be true), + // or it could be a G-code (again, is_external will be true). + std::string file; + + // Has this profile been loaded? + bool loaded = false; + + // Configuration data, loaded from a file, or set from the defaults. + DynamicPrintConfig config; + + // Load this profile for the following keys only. + // Throws std::runtime_error in case the file cannot be read. + DynamicPrintConfig& load(const std::vector &keys); + + // Set the is_dirty flag if the provided config is different from the active one. + bool set_dirty(const DynamicPrintConfig &config) { this->is_dirty = ! this->config.diff(config).empty(); } + void reset_dirty() { this->is_dirty = false; } + bool enable_compatible(const std::string &active_printer); +}; + +// Collections of presets of the same type (one of the Print, Filament or Printer type). +class PresetCollection +{ +public: + // Initialize the PresetCollection with the "- default -" preset. + PresetCollection(Preset::Type type, const std::vector &keys); + + // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. + void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } + void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; } + + // Enable / disable the "- default -" preset. + void set_default_suppressed(bool default_suppressed); + bool is_default_suppressed() const { return m_default_suppressed; } + + // Select a preset. If an invalid index is provided, the first visible preset is selected. + Preset& select_preset(size_t idx); + // Return the selected preset, without the user modifications applied. + Preset& get_selected_preset() { return m_presets[m_idx_selected]; } + const Preset& get_selected_preset() const { return m_presets[m_idx_selected]; } + // Return the selected preset including the user modifications. + Preset& get_edited_preset() { return m_edited_preset; } + const Preset& get_edited_preset() const { return m_edited_preset; } + // Return a preset possibly with modifications. + Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; } + const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } + size_t size() const { return this->m_presets.size(); } + + // For Print / Filament presets, disable those, which are not compatible with the printer. + void enable_disable_compatible_to_printer(const std::string &active_printer); + + size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); } + void delete_preset(const size_t idx); + + // Update the choice UI from the list of presets. + void update_editor_ui(wxBitmapComboBox *ui); + void update_platter_ui(wxBitmapComboBox *ui); + +private: + // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. + Preset::Type m_type; + // List of presets, starting with the "- default -" preset. + std::vector m_presets; + Preset m_edited_preset; + // Selected preset. + int m_idx_selected; + // Is the "- default -" preset suppressed? + bool m_default_suppressed = true; + // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. + const wxBitmap *m_bitmap_compatible = nullptr; + const wxBitmap *m_bitmap_incompatible = nullptr; +}; + +// Bundle of Print + Filament + Printer presets. +class PresetBundle +{ +public: + PresetBundle(); + ~PresetBundle(); + + bool load_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); + + PresetCollection prints; + PresetCollection filaments; + PresetCollection printers; + + // Update the colors preview at the platter extruder combo box. + void update_platter_filament_ui_colors(wxBitmapComboBox *ui, unsigned int idx_extruder, unsigned int idx_filament); + + static const std::vector& print_options(); + static const std::vector& filament_options(); + static const std::vector& printer_options(); + +private: + // Indicator, that the preset is compatible with the selected printer. + wxBitmap *m_bitmapCompatible; + // Indicator, that the preset is NOT compatible with the selected printer. + wxBitmap *m_bitmapIncompatible; + // Caching color bitmaps for the + std::map m_mapColorToBitmap; +}; + +} // namespace Slic3r + +#endif /* slic3r_Preset_hpp_ */ diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp new file mode 100644 index 000000000..938a86373 --- /dev/null +++ b/xs/xsp/GUI_Preset.xsp @@ -0,0 +1,56 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/Preset.hpp" +%} + +%name{Slic3r::GUI::Preset} class Preset { + // owned by PresetCollection, no constructor/destructor + + bool is_default() %code%{ RETVAL = THIS->is_default; %}; + bool is_external() %code%{ RETVAL = THIS->is_external; %}; + bool is_visible() %code%{ RETVAL = THIS->is_visible; %}; + bool is_dirty() %code%{ RETVAL = THIS->is_dirty; %}; + + const char* name() %code%{ RETVAL = THIS->name.c_str(); %}; + const char* file() %code%{ RETVAL = THIS->file.c_str(); %}; + + bool loaded() %code%{ RETVAL = THIS->loaded; %}; + + Ref config() %code%{ RETVAL = &THIS->config; %}; +}; + +%name{Slic3r::GUI::PresetCollection} class PresetCollection { + + Ref preset(size_t idx) %code%{ RETVAL = &THIS->preset(idx); %}; + size_t size() const; + size_t num_visible() const; +%{ + +SV* +PresetCollection::arrayref() + CODE: + AV* av = newAV(); + av_fill(av, THIS->size()-1); + int i = 0; + for (size_t i = 0; i < THIS->size(); ++ i) { + Preset &preset = THIS->preset(i); + av_store(av, i++, perl_to_SV_ref(preset)); + } + RETVAL = newRV_noinc((SV*)av); + OUTPUT: + RETVAL + +%} +}; + + +%name{Slic3r::GUI::PresetBundle} class PresetBundle { + PresetBundle(); + ~PresetBundle(); + + Ref prints() %code%{ RETVAL = &THIS->prints; %}; + Ref filaments() %code%{ RETVAL = &THIS->filaments; %}; + Ref printers() %code%{ RETVAL = &THIS->printers; %}; +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 31d6c3eea..db94cd15f 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -216,6 +216,13 @@ Ref O_OBJECT_SLIC3R_T GLVolumeCollection* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T +Preset* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +PresetCollection* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +PresetBundle* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + Axis T_UV ExtrusionLoopRole T_UV ExtrusionRole T_UV diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index d536210bb..87595415d 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -196,6 +196,12 @@ %typemap{Ref}{simple}; %typemap{GLVolumeCollection*}; %typemap{Ref}{simple}; +%typemap{Preset*}; +%typemap{Ref}{simple}; +%typemap{PresetCollection*}; +%typemap{Ref}{simple}; +%typemap{PresetBundle*}; +%typemap{Ref}{simple}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*};