diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bc37300269..9e5d5c4fab 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1,4 +1,5 @@ #include "libslic3r.h" +#include "I18N.hpp" #include "GCode.hpp" #include "ExtrusionEntity.hpp" #include "EdgeGrid.hpp" @@ -36,6 +37,11 @@ namespace Slic3r { +//! macro used to mark string used at localization, +//! return same string +#define L(s) (s) +#define _(s) Slic3r::I18N::translate(s) + // Only add a newline in case the current G-code does not end with a newline. static inline void check_add_eol(std::string &gcode) { @@ -405,7 +411,8 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size()); if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { if (m_layer_idx < (int)m_tool_changes.size()) { - assert(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].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); } m_brim_done = true; @@ -448,6 +455,17 @@ std::vector GCode::collect_layers_to_print(const PrintObjec -- idx_object_layer; } } + + // Let's make sure that the last layer is not empty, so we don't build on top of it. + if (! layers_to_print.empty() + && ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) + || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions())) + && (! layers_to_print.back().object_layer || ! layers_to_print.back().object_layer->has_extrusions()) + && (! layers_to_print.back().support_layer || ! layers_to_print.back().support_layer->has_extrusions())) + throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" + + _(L("Object name: ")) + object.model_object()->name + "\n" + _(L("Print z: ")) + + std::to_string(layers_to_print.back().print_z())); + layers_to_print.emplace_back(layer_to_print); } @@ -1138,9 +1156,9 @@ void GCode::_do_export(Print &print, FILE *file) print.m_print_statistics.clear(); print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; - print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms(); + print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms(true); if (m_silent_time_estimator_enabled) - print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms(); + print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms(true); std::vector extruders = m_writer.extruders(); if (! extruders.empty()) { diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index f10b45723a..18437c8e16 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -78,8 +78,13 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool zs.emplace_back(layer->print_z); for (auto layer : object->support_layers()) zs.emplace_back(layer->print_z); - if (! object->layers().empty()) - object_bottom_z = object->layers().front()->print_z - object->layers().front()->height; + + // Find first object layer that is not empty and save its print_z + for (const Layer* layer : object->layers()) + if (layer->has_extrusions()) { + object_bottom_z = layer->print_z - layer->height; + break; + } } this->initialize_layers(zs); } @@ -324,6 +329,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ m_layer_tools[j].has_wipe_tower = true; } else { LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); + //LayerTools <_prev = m_layer_tools[j]; LayerTools <_next = m_layer_tools[j + 1]; assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty()); // FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now. diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 55a6c4437e..e1b34fdeae 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1242,6 +1242,8 @@ void WipeTower::generate(std::vector> & for (auto& used : m_used_filament_length) // reset used filament stats used = 0.f; + m_old_temperature = -1; // reset last temperature written in the gcode + std::vector layer_result; for (auto layer : m_plan) { diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 33dc9f4b7d..8a2e1266a8 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -694,22 +694,39 @@ namespace Slic3r { return m_color_times; } - std::vector GCodeTimeEstimator::get_color_times_dhms() const + std::vector GCodeTimeEstimator::get_color_times_dhms(bool include_remaining) const { std::vector ret; + float total_time = 0.0f; for (float t : m_color_times) { - ret.push_back(_get_time_dhms(t)); + std::string time = _get_time_dhms(t); + if (include_remaining) + { + time += " ("; + time += _get_time_dhms(m_time - total_time); + time += ")"; + } + total_time += t; + ret.push_back(time); } return ret; } - std::vector GCodeTimeEstimator::get_color_times_minutes() const + std::vector GCodeTimeEstimator::get_color_times_minutes(bool include_remaining) const { std::vector ret; + float total_time = 0.0f; for (float t : m_color_times) { - ret.push_back(_get_time_minutes(t)); + std::string time = _get_time_minutes(t); + if (include_remaining) + { + time += " ("; + time += _get_time_minutes(m_time - total_time); + time += ")"; + } + total_time += t; } return ret; } diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index 840d587784..8d794af1e4 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -346,14 +346,16 @@ namespace Slic3r { // Returns the estimated time, in minutes (integer) std::string get_time_minutes() const; - // Returns the estimated time, in seconds, for each color + // Returns the estimated time, in seconds, for each color std::vector get_color_times() const; // Returns the estimated time, in format DDd HHh MMm SSs, for each color - std::vector get_color_times_dhms() const; + // If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)" + std::vector get_color_times_dhms(bool include_remaining) const; // Returns the estimated time, in minutes (integer), for each color - std::vector get_color_times_minutes() const; + // If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)" + std::vector get_color_times_minutes(bool include_remaining) const; // Return an estimate of the memory consumed by the time estimator. size_t memory_used() const; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 25cd92d865..d6489d6780 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -99,8 +99,7 @@ Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *c result = load_stl(input_file.c_str(), &model); else if (boost::algorithm::iends_with(input_file, ".obj")) result = load_obj(input_file.c_str(), &model); - else if (!boost::algorithm::iends_with(input_file, ".zip.amf") && (boost::algorithm::iends_with(input_file, ".amf") || - boost::algorithm::iends_with(input_file, ".amf.xml"))) + else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml")) result = load_amf(input_file.c_str(), config, &model); else if (boost::algorithm::iends_with(input_file, ".3mf")) result = load_3mf(input_file.c_str(), config, &model); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c423afeb94..e53f498106 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1723,7 +1723,7 @@ void Print::_make_wipe_tower() break; lt.has_support = true; // Insert the new support layer. - double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z; + double height = lt.print_z - (i == 0 ? 0. : m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z); //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway. it_layer = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height); ++ it_layer; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 304f6f749f..e49c81f460 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -422,6 +422,7 @@ void PrintConfigDef::init_fff_params() def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values = def_top_fill_pattern->enum_values; + def->enum_labels = def_top_fill_pattern->enum_labels; def->aliases = def_top_fill_pattern->aliases; def->set_default_value(new ConfigOptionEnum(ipRectilinear)); diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 242d00a071..8e3a6d1f12 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -22,10 +22,10 @@ namespace Slic3r { namespace GUI { const double Camera::DefaultDistance = 1000.0; -double Camera::FrustrumMinZSize = 50.0; +double Camera::FrustrumMinZRange = 50.0; +double Camera::FrustrumMinNearZ = 100.0; double Camera::FrustrumZMargin = 10.0; -double Camera::FovMinDeg = 0.5; -double Camera::FovMaxDeg = 75.0; +double Camera::MaxFovDeg = 60.0; Camera::Camera() : phi(45.0f) @@ -186,7 +186,8 @@ void Camera::apply_view_matrix() const void Camera::apply_projection(const BoundingBoxf3& box) const { - m_distance = DefaultDistance; + set_distance(DefaultDistance); + double w = 0.0; double h = 0.0; @@ -194,15 +195,14 @@ void Camera::apply_projection(const BoundingBoxf3& box) const { m_frustrum_zs = calc_tight_frustrum_zs_around(box); - w = (double)m_viewport[2]; - h = (double)m_viewport[3]; + w = 0.5 * (double)m_viewport[2]; + h = 0.5 * (double)m_viewport[3]; - double two_zoom = 2.0 * m_zoom; - if (two_zoom != 0.0) + if (m_zoom != 0.0) { - double inv_two_zoom = 1.0 / two_zoom; - w *= inv_two_zoom; - h *= inv_two_zoom; + double inv_zoom = 1.0 / m_zoom; + w *= inv_zoom; + h *= inv_zoom; } switch (m_type) @@ -226,21 +226,16 @@ void Camera::apply_projection(const BoundingBoxf3& box) const if (m_type == Perspective) { - double fov_rad = 2.0 * std::atan(h / m_frustrum_zs.first); - double fov_deg = Geometry::rad2deg(fov_rad); + double fov_deg = Geometry::rad2deg(2.0 * std::atan(h / m_frustrum_zs.first)); // adjust camera distance to keep fov in a limited range - if (fov_deg > FovMaxDeg + 0.001) + if (fov_deg > MaxFovDeg) { - double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMaxDeg)); - m_distance += (new_near_z - m_frustrum_zs.first); - apply_view_matrix(); - } - else if (fov_deg < FovMinDeg - 0.001) - { - double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMinDeg)); - m_distance += (new_near_z - m_frustrum_zs.first); - apply_view_matrix(); + double delta_z = h / ::tan(0.5 * Geometry::deg2rad(MaxFovDeg)) - m_frustrum_zs.first; + if (delta_z > 0.001) + set_distance(m_distance + delta_z); + else + break; } else break; @@ -328,42 +323,50 @@ void Camera::debug_render() const std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const { - std::pair ret = std::make_pair(DBL_MAX, -DBL_MAX); + std::pair ret; - Vec3d bb_min = box.min; - Vec3d bb_max = box.max; - - // box vertices in world space - std::vector vertices; - vertices.reserve(8); - vertices.push_back(bb_min); - vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2)); - vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2)); - vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2)); - vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2)); - vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2)); - vertices.push_back(bb_max); - vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2)); - - // set the Z range in eye coordinates (negative Zs are in front of the camera) - for (const Vec3d& v : vertices) + while (true) { - double z = -(m_view_matrix * v)(2); - ret.first = std::min(ret.first, z); - ret.second = std::max(ret.second, z); - } + ret = std::make_pair(DBL_MAX, -DBL_MAX); - // apply margin - ret.first -= FrustrumZMargin; - ret.second += FrustrumZMargin; + // box vertices in world space + std::vector vertices; + vertices.reserve(8); + vertices.push_back(box.min); + vertices.emplace_back(box.max(0), box.min(1), box.min(2)); + vertices.emplace_back(box.max(0), box.max(1), box.min(2)); + vertices.emplace_back(box.min(0), box.max(1), box.min(2)); + vertices.emplace_back(box.min(0), box.min(1), box.max(2)); + vertices.emplace_back(box.max(0), box.min(1), box.max(2)); + vertices.push_back(box.max); + vertices.emplace_back(box.min(0), box.max(1), box.max(2)); - // ensure min size - if (ret.second - ret.first < FrustrumMinZSize) - { - double mid_z = 0.5 * (ret.first + ret.second); - double half_size = 0.5 * FrustrumMinZSize; - ret.first = mid_z - half_size; - ret.second = mid_z + half_size; + // set the Z range in eye coordinates (negative Zs are in front of the camera) + for (const Vec3d& v : vertices) + { + double z = -(m_view_matrix * v)(2); + ret.first = std::min(ret.first, z); + ret.second = std::max(ret.second, z); + } + + // apply margin + ret.first -= FrustrumZMargin; + ret.second += FrustrumZMargin; + + // ensure min size + if (ret.second - ret.first < FrustrumMinZRange) + { + double mid_z = 0.5 * (ret.first + ret.second); + double half_size = 0.5 * FrustrumMinZRange; + ret.first = mid_z - half_size; + ret.second = mid_z + half_size; + } + + if (ret.first >= FrustrumMinNearZ) + break; + + // ensure min Near Z + set_distance(m_distance + FrustrumMinNearZ - ret.first); } return ret; @@ -385,21 +388,19 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca Vec3d up = get_dir_up(); Vec3d forward = get_dir_forward(); - Vec3d bb_min = box.min; - Vec3d bb_max = box.max; Vec3d bb_center = box.center(); // box vertices in world space std::vector vertices; vertices.reserve(8); - vertices.push_back(bb_min); - vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2)); - vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2)); - vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2)); - vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2)); - vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2)); - vertices.push_back(bb_max); - vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2)); + vertices.push_back(box.min); + vertices.emplace_back(box.max(0), box.min(1), box.min(2)); + vertices.emplace_back(box.max(0), box.max(1), box.min(2)); + vertices.emplace_back(box.min(0), box.max(1), box.min(2)); + vertices.emplace_back(box.min(0), box.min(1), box.max(2)); + vertices.emplace_back(box.max(0), box.min(1), box.max(2)); + vertices.push_back(box.max); + vertices.emplace_back(box.min(0), box.max(1), box.max(2)); double max_x = 0.0; double max_y = 0.0; @@ -430,6 +431,12 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y)); } +void Camera::set_distance(double distance) const +{ + m_distance = distance; + apply_view_matrix(); +} + } // GUI } // Slic3r diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 79e87c7264..839d0d6cf1 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -10,10 +10,10 @@ namespace GUI { struct Camera { static const double DefaultDistance; - static double FrustrumMinZSize; + static double FrustrumMinZRange; + static double FrustrumMinNearZ; static double FrustrumZMargin; - static double FovMinDeg; - static double FovMaxDeg; + static double MaxFovDeg; enum EType : unsigned char { @@ -101,6 +101,7 @@ private: // the camera MUST be outside of the bounding box in eye coordinate of the given box std::pair calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const; double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const; + void set_distance(double distance) const; }; } // GUI diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index 7865aecf2e..d1f2da040c 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -354,7 +354,7 @@ bool FirmwareDialog::priv::check_model_id() // Therefore, regretably, so far the check cannot be used and we just return true here. // TODO: Rewrite Serial using more platform-native code. return true; - + // if (hex_file.model_id.empty()) { // // No data to check against, assume it's ok // return true; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5e505bb416..c74207123d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1529,7 +1529,7 @@ void GLCanvas3D::render() } m_camera.apply_view_matrix(); - m_camera.apply_projection(_max_bounding_box(true)); + m_camera.apply_projection(_max_bounding_box(true, true)); GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam)); @@ -3273,7 +3273,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) void GLCanvas3D::set_camera_zoom(double zoom) { const Size& cnv_size = get_canvas_size(); - m_camera.set_zoom(zoom, _max_bounding_box(false), cnv_size.get_width(), cnv_size.get_height()); + m_camera.set_zoom(zoom, _max_bounding_box(false, false), cnv_size.get_width(), cnv_size.get_height()); m_dirty = true; } @@ -3699,9 +3699,20 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) m_dirty = false; } -BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_bed_model) const +BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const { BoundingBoxf3 bb = volumes_bounding_box(); + + // The following is a workaround for gizmos not being taken in account when calculating the tight camera frustrum + // A better solution would ask the gizmo manager for the bounding box of the current active gizmo, if any + if (include_gizmos && m_gizmos.is_running()) + { + BoundingBoxf3 sel_bb = m_selection.get_bounding_box(); + Vec3d sel_bb_center = sel_bb.center(); + Vec3d extend_by = sel_bb.max_size() * Vec3d::Ones(); + bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by)); + } + bb.merge(m_bed.get_bounding_box(include_bed_model)); return bb; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index c9803f9db8..bd33dbef78 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -652,7 +652,7 @@ private: bool _set_current(); void _resize(unsigned int w, unsigned int h); - BoundingBoxf3 _max_bounding_box(bool include_bed_model) const; + BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const; void _zoom_to_box(const BoundingBoxf3& box); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c0af7a41e7..c714b6797c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -494,7 +494,7 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const if (obj_idx < 0) return; const int volume_id = m_objects_model->GetVolumeIdByItem(item); - take_snapshot(wxString::Format(_(L("Rename %s")), volume_id < 0 ? _(L("Object")) : _(L("Sub-object")))); + take_snapshot(volume_id < 0 ? _(L("Rename Object")) : _(L("Rename Sub-object"))); if (m_objects_model->GetItemType(item) & itObject) { (*m_objects)[obj_idx]->name = m_objects_model->GetName(item).ToUTF8().data(); @@ -1049,8 +1049,10 @@ void ObjectList::get_settings_choice(const wxString& category_name) wxArrayInt selections; wxDataViewItem item = GetSelection(); + const ItemType item_type = m_objects_model->GetItemType(item); + settings_menu_hierarchy settings_menu; - const bool is_part = m_objects_model->GetItemType(item) & (itVolume | itLayer); + const bool is_part = item_type & (itVolume | itLayer); get_options_menu(settings_menu, is_part); std::vector< std::pair > *settings_list = nullptr; @@ -1127,7 +1129,10 @@ void ObjectList::get_settings_choice(const wxString& category_name) } #endif - take_snapshot(wxString::Format(_(L("Add Settings for %s")), is_part ? _(L("Sub-object")) : _(L("Object")))); + const wxString snapshot_text = item_type & itLayer ? _(L("Add Settings for Layers")) : + item_type & itVolume ? _(L("Add Settings for Sub-object")) : + _(L("Add Settings for Object")); + take_snapshot(snapshot_text); std::vector selected_options; selected_options.reserve(selection_cnt); @@ -1159,7 +1164,7 @@ void ObjectList::get_settings_choice(const wxString& category_name) // Add settings item for object/sub-object and show them - if (!(m_objects_model->GetItemType(item) & (itObject | itVolume | itLayer))) + if (!(item_type & (itObject | itVolume | itLayer))) item = m_objects_model->GetTopParent(item); show_settings(add_settings_item(item, m_config)); } @@ -1169,11 +1174,13 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) std::vector options = get_options_for_bundle(bundle_name); wxDataViewItem item = GetSelection(); + ItemType item_type = m_objects_model->GetItemType(item); + /* Because of we couldn't edited layer_height for ItVolume from settings list, * correct options according to the selected item type : * remove "layer_height" option */ - if ((m_objects_model->GetItemType(item) & itVolume) && bundle_name == _("Layers and Perimeters")) { + if ((item_type & itVolume) && bundle_name == _("Layers and Perimeters")) { const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height"); if (layer_height_it != options.end()) options.erase(layer_height_it); @@ -1185,7 +1192,10 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) assert(m_config); auto opt_keys = m_config->keys(); - take_snapshot(wxString::Format(_(L("Add Settings Bundle for %s")), m_objects_model->GetItemType(item) & (itVolume|itLayer) ? _(L("Sub-object")) : _(L("Object")))); + const wxString snapshot_text = item_type & itLayer ? _(L("Add Settings Bundle for Layers")) : + item_type & itVolume ? _(L("Add Settings Bundle for Sub-object")) : + _(L("Add Settings Bundle for Object")); + take_snapshot(snapshot_text); const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; for (auto& opt_key : options) @@ -1202,7 +1212,7 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) } // Add settings item for object/sub-object and show them - if (!(m_objects_model->GetItemType(item) & (itObject | itVolume | itLayer))) + if (!(item_type & (itObject | itVolume | itLayer))) item = m_objects_model->GetTopParent(item); show_settings(add_settings_item(item, m_config)); } @@ -1217,26 +1227,6 @@ void ObjectList::show_settings(const wxDataViewItem settings_item) // update object selection on Plater if (!m_prevent_canvas_selection_update) update_selections_on_canvas(); -/* auto item = GetSelection(); - if (item) { - if (m_objects_model->GetItemType(item) == itInstance) - item = m_objects_model->GetTopParent(item); - const auto settings_item = m_objects_model->IsSettingsItem(item) ? item : m_objects_model->GetSettingsItem(item); - select_item(settings_item ? settings_item : - m_objects_model->AddSettingsChild(item)); - - // update object selection on Plater - if (!m_prevent_canvas_selection_update) - update_selections_on_canvas(); - } - else { - //# ys_FIXME ??? use case ??? - auto panel = wxGetApp().sidebar().scrolled_panel(); - panel->Freeze(); - wxGetApp().obj_settings()->UpdateAndShow(true); - panel->Thaw(); - } - */ } wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) { @@ -1778,7 +1768,19 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0) m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item)); + // If last two Instances of object is selected, show the message about impossible action + bool show_msg = false; + if (type & itInstance) { + wxDataViewItemArray instances; + m_objects_model->GetChildren(m_objects_model->GetParent(item), instances); + if (instances.Count() == 2 && IsSelected(instances[0]) && IsSelected(instances[1])) + show_msg = true; + } + m_objects_model->Delete(item); + + if (show_msg) + Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last intance from object."))); } void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) @@ -1861,7 +1863,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con if (vol->is_model_part()) ++solid_cnt; if (volume->is_model_part() && solid_cnt == 1) { - Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from object."))); + Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last solid part from object."))); return false; } @@ -1880,7 +1882,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con } else if (type == itInstance) { if (object->instances.size() == 1) { - Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object."))); + Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last intance from object."))); return false; } @@ -2435,6 +2437,8 @@ void ObjectList::remove() for (auto& item : sels) { + if (m_objects_model->InvalidItem(item)) // item can be deleted for this moment (like last 2 Instances or Volumes) + continue; if (m_objects_model->GetParent(item) == wxDataViewItem(0)) delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1); else { @@ -3181,33 +3185,6 @@ void ObjectList::last_volume_is_deleted(const int obj_idx) volume->config.set_key_value("extruder", new ConfigOptionInt(0)); } -/* #lm_FIXME_delete_after_testing -void ObjectList::update_settings_items() -{ - m_prevent_canvas_selection_update = true; - wxDataViewItemArray sel; - GetSelections(sel); // stash selection - - wxDataViewItemArray items; - m_objects_model->GetChildren(wxDataViewItem(0), items); - - for (auto& item : items) { - const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item); - select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item)); - - // If settings item was deleted from the list, - // it's need to be deleted from selection array, if it was there - if (settings_item != m_objects_model->GetSettingsItem(item) && - sel.Index(settings_item) != wxNOT_FOUND) { - sel.Remove(settings_item); - } - } - - // restore selection: - SetSelections(sel); - m_prevent_canvas_selection_update = false; -} -*/ void ObjectList::update_and_show_object_settings_item() { const wxDataViewItem item = GetSelection(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d3da148a05..f5da43aee9 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -279,6 +279,18 @@ bool MainFrame::can_export_gcode() const return true; } +bool MainFrame::can_send_gcode() const +{ + if (m_plater == nullptr) + return false; + + if (m_plater->model().objects.empty()) + return false; + + const auto prin_host_opt =wxGetApp().preset_bundle->printers.get_edited_preset().config.option("print_host"); + return prin_host_opt != nullptr && !prin_host_opt->value.empty(); +} + bool MainFrame::can_slice() const { bool bg_proc = wxGetApp().app_config->get("background_processing") == "1"; @@ -451,6 +463,10 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, menu_icon("export_gcode"), nullptr, [this](){return can_export_gcode(); }, this); m_changeable_menu_items.push_back(item_export_gcode); + wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")), + [this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, menu_icon("export_gcode"), nullptr, + [this](){return can_send_gcode(); }, this); + m_changeable_menu_items.push_back(item_send_gcode); export_menu->AppendSeparator(); append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"), nullptr, @@ -689,7 +705,8 @@ void MainFrame::update_menubar() { const bool is_fff = plater()->printer_technology() == ptFFF; - m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("Export")) ) + dots + "\tCtrl+G"); + m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("E&xport")) ) + dots + "\tCtrl+G"); + m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G"); m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3"); m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, menu_icon(is_fff ? "spool": "resin"))); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index a41f33824d..5d34be48ef 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -67,6 +67,7 @@ class MainFrame : public DPIFrame bool can_export_model() const; bool can_export_supports() const; bool can_export_gcode() const; + bool can_send_gcode() const; bool can_slice() const; bool can_change_view() const; bool can_select() const; @@ -79,6 +80,7 @@ class MainFrame : public DPIFrame enum MenuItems { // FFF SLA miExport = 0, // Export G-code Export + miSend, // Send G-code Send to print miMaterialTab, // Filament Settings Material Settings }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1a49466378..ea74cdf208 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1175,10 +1175,10 @@ void Sidebar::show_sliced_info_sizer(const bool show) if (ps.estimated_silent_print_time != "N/A") { new_label += wxString::Format("\n - %s", _(L("stealth mode"))); info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); - for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i) + for (int i = (int)ps.estimated_silent_color_print_times.size() - 1; i >= 0; --i) { new_label += wxString::Format("\n - %s%d", _(L("Color ")), i + 1); - info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]); + info_text += wxString::Format("\n%s", ps.estimated_silent_color_print_times[i]); } } p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 630ecd1d4a..85f154e959 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -402,8 +402,10 @@ void Selection::remove_all() if (is_empty()) return; - - if (!wxGetApp().plater()->can_redo()) + +// Not taking the snapshot with non-empty Redo stack will likely be more confusing than losing the Redo stack. +// Let's wait for user feedback. +// if (!wxGetApp().plater()->can_redo()) wxGetApp().plater()->take_snapshot(_(L("Selection-Remove All"))); m_mode = Instance; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 7ddf1c2732..3da6997a79 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1385,6 +1385,18 @@ int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const return -1; } +bool ObjectDataViewModel::InvalidItem(const wxDataViewItem& item) +{ + if (!item) + return true; + + ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); + if (!node || node->invalid()) + return true; + + return false; +} + wxString ObjectDataViewModel::GetName(const wxDataViewItem &item) const { ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index b04c6c9c06..5b9bdee023 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -367,6 +367,7 @@ public: #ifndef NDEBUG bool valid(); #endif /* NDEBUG */ + bool invalid() const { return m_idx < -1; } private: friend class ObjectDataViewModel; @@ -430,6 +431,7 @@ public: void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); int GetRowByItem(const wxDataViewItem& item) const; bool IsEmpty() { return m_objects.empty(); } + bool InvalidItem(const wxDataViewItem& item); // helper method for wxLog diff --git a/src/slic3r/Utils/Serial.cpp b/src/slic3r/Utils/Serial.cpp index cd2a01cbfd..acfd5fafd0 100644 --- a/src/slic3r/Utils/Serial.cpp +++ b/src/slic3r/Utils/Serial.cpp @@ -353,6 +353,8 @@ void Serial::set_baud_rate(unsigned baud_rate) } } + +/* void Serial::set_DTR(bool on) { auto handle = native_handle(); @@ -495,6 +497,7 @@ std::string Serial::printer_format_line(const std::string &line, unsigned line_n return (boost::format("N%1% %2%*%3%\n") % line_num_str % line % checksum).str(); } +*/ } // namespace Utils diff --git a/src/slic3r/Utils/Serial.hpp b/src/slic3r/Utils/Serial.hpp index 67d64b4ec1..8bad75b315 100644 --- a/src/slic3r/Utils/Serial.hpp +++ b/src/slic3r/Utils/Serial.hpp @@ -46,6 +46,17 @@ public: ~Serial(); void set_baud_rate(unsigned baud_rate); + + // The Serial implementation is currently in disarray and therefore commented out. + // The boost implementation seems to have several problems, such as lack of support + // for custom baud rates, few weird implementation bugs and a history of API breakages. + // It's questionable whether it solves more problems than causes. Probably not. + // TODO: Custom implementation not based on asio. + // + // As of now, this class is only kept for the purpose of rebooting AVR109, + // see FirmwareDialog::priv::avr109_reboot() + +/* void set_DTR(bool on); // Resets the line number both internally as well as with the firmware using M110 @@ -68,7 +79,7 @@ public: // Same as above, but with internally-managed line number size_t printer_write_line(const std::string &line); - + // Toggles DTR to reset the printer void printer_reset(); @@ -76,6 +87,7 @@ public: static std::string printer_format_line(const std::string &line, unsigned line_num); private: unsigned m_line_num = 0; +*/ };