diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index af9bb4f98..ddf377c78 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -151,8 +151,8 @@ bool stl_write_binary(stl_file *stl, const char *file, const char *label) memcpy(buffer, &stl->stats.number_of_facets, 4); stl_internal_reverse_quads(buffer, 4); fwrite(buffer, 4, 1, fp); - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { - memcpy(buffer, stl->facet_start + i, 50); + for (const stl_facet &facet : stl->facet_start) { + memcpy(buffer, &facet, 50); // Convert to little endian. stl_internal_reverse_quads(buffer, 48); fwrite(buffer, SIZEOF_STL_FACET, 1, fp); diff --git a/src/avrdude/ChangeLog b/src/avrdude/ChangeLog index 975f52317..879fbf957 100644 --- a/src/avrdude/ChangeLog +++ b/src/avrdude/ChangeLog @@ -1,3 +1,12 @@ +2018-01-17 Joerg Wunsch +(cherry-picked) + Submitted by Reinhard Max + patch #8311: Add IPv6 support to the -Pnet:host:port option + * ser_posix.c (net_open): Rewrite to use getaddrinfo() + rather than gethostbyname() + * avrdude.1: Document IPv6 feature + * doc/avrdude.texi: (Dito) + 2016-05-10 Joerg Wunsch Submitted by Hannes Jochriem: diff --git a/src/avrdude/avrdude.1 b/src/avrdude/avrdude.1 index 65fc7b1d6..47ca4edde 100644 --- a/src/avrdude/avrdude.1 +++ b/src/avrdude/avrdude.1 @@ -505,12 +505,19 @@ network connection to (TCP) on .Ar host is established. +Square brackets may be placed around +.Ar host +to improve readability, for numeric IPv6 addresses (e.g. +.Li net:[2001:db8::42]:1337 ) . The remote endpoint is assumed to be a terminal or console server that connects the network stream to a local serial port where the actual programmer has been attached to. The port is assumed to be properly configured, for example using a transparent 8-bit data connection without parity at 115200 Baud for a STK500. +.Pp +Note: The ability to handle IPv6 hostnames and addresses is limited to +Posix systems (by now). .It Fl q Disable (or quell) output of the progress bar while reading or writing to the device. Specify it a second time for even quieter operation. diff --git a/src/avrdude/configure.ac b/src/avrdude/configure.ac index a23a959f2..d14fc545f 100644 --- a/src/avrdude/configure.ac +++ b/src/avrdude/configure.ac @@ -214,7 +214,7 @@ AC_HEADER_TIME AC_CHECK_LIB([ws2_32], [puts]) # Checks for library functions. -AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep]) +AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep getaddrinfo]) AC_MSG_CHECKING([for a Win32 HID libray]) SAVED_LIBS="${LIBS}" diff --git a/src/avrdude/doc/avrdude.texi b/src/avrdude/doc/avrdude.texi index 6941389df..7062a9920 100644 --- a/src/avrdude/doc/avrdude.texi +++ b/src/avrdude/doc/avrdude.texi @@ -557,6 +557,9 @@ higher level protocol (as opposed to bit-bang style programmers), In this case, instead of trying to open a local device, a TCP network connection to (TCP) @var{port} on @var{host} is established. +Square brackets may be placed around @var{host} to improve +readability for numeric IPv6 addresses (e.g. +@code{net:[2001:db8::42]:1337}). The remote endpoint is assumed to be a terminal or console server that connects the network stream to a local serial port where the actual programmer has been attached to. @@ -564,6 +567,8 @@ The port is assumed to be properly configured, for example using a transparent 8-bit data connection without parity at 115200 Baud for a STK500. +Note: The ability to handle IPv6 hostnames and addresses is limited to +Posix systems (by now). @item -q Disable (or quell) output of the progress bar while reading or writing diff --git a/src/avrdude/ser_posix.c b/src/avrdude/ser_posix.c index 9992f78e3..dfa02f9fe 100644 --- a/src/avrdude/ser_posix.c +++ b/src/avrdude/ser_posix.c @@ -150,6 +150,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud) return 0; } +#include "ac_cfg.h" // Timeout read & write variants // Additionally to the regular -1 on I/O error, they return -2 on timeout @@ -221,23 +222,35 @@ ssize_t write_timeout(int fd, const void *buf, size_t count, long timeout) static int net_open(const char *port, union filedescriptor *fdp) { - char *hstr, *pstr, *end; - unsigned int pnum; - int fd; - struct sockaddr_in sockaddr; - struct hostent *hp; +#ifdef HAVE_GETADDRINFO + char *hp, *hstr, *pstr; + int s, fd, ret = -1; + struct addrinfo hints; + struct addrinfo *result, *rp; - if ((hstr = strdup(port)) == NULL) { + if ((hstr = hp = strdup(port)) == NULL) { avrdude_message(MSG_INFO, "%s: net_open(): Out of memory!\n", progname); return -1; } - if (((pstr = strchr(hstr, ':')) == NULL) || (pstr == hstr)) { + /* + * As numeric IPv6 addresses use colons as separators, we need to + * look for the last colon here, which separates the port number or + * service name from the host or IP address. + */ + if (((pstr = strrchr(hstr, ':')) == NULL) || (pstr == hstr)) { avrdude_message(MSG_INFO, "%s: net_open(): Mangled host:port string \"%s\"\n", progname, hstr); - free(hstr); - return -1; + goto error; + } + + /* + * Remove brackets from the host part, if present. + */ + if (*hstr == '[' && *(pstr-1) == ']') { + hstr++; + *(pstr-1) = '\0'; } /* @@ -245,43 +258,49 @@ net_open(const char *port, union filedescriptor *fdp) */ *pstr++ = '\0'; - pnum = strtoul(pstr, &end, 10); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + s = getaddrinfo(hstr, pstr, &hints, &result); - if ((*pstr == '\0') || (*end != '\0') || (pnum == 0) || (pnum > 65535)) { - avrdude_message(MSG_INFO, "%s: net_open(): Bad port number \"%s\"\n", - progname, pstr); - free(hstr); - return -1; + if (s != 0) { + avrdude_message(MSG_INFO, + "%s: net_open(): Cannot resolve " + "host=\"%s\", port=\"%s\": %s\n", + progname, hstr, pstr, gai_strerror(s)); + goto error; } - - if ((hp = gethostbyname(hstr)) == NULL) { - avrdude_message(MSG_INFO, "%s: net_open(): unknown host \"%s\"\n", - progname, hstr); - free(hstr); - return -1; + for (rp = result; rp != NULL; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + /* This one failed, loop over */ + continue; + } + if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) { + /* Success, we are connected */ + break; + } + close(fd); } - - free(hstr); - - if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n", - progname, strerror(errno)); - return -1; + if (rp == NULL) { + avrdude_message(MSG_INFO, "%s: net_open(): Cannot connect: %s\n", + progname, strerror(errno)); } - - memset(&sockaddr, 0, sizeof(struct sockaddr_in)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(pnum); - memcpy(&(sockaddr.sin_addr.s_addr), hp->h_addr, sizeof(struct in_addr)); - - if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) { - avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n", - progname, strerror(errno)); - return -1; + else { + fdp->ifd = fd; + ret = 0; } + freeaddrinfo(result); - fdp->ifd = fd; - return 0; +error: + free(hp); + return ret; +#else + avrdude_message(MSG_INFO, + "%s: Networking is not supported on your platform.\n" + "If you need it, please open a bug report.\n", progname); + return -1; +#endif /* HAVE_GETADDRINFO */ } diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 9ae116c47..70c2348af 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -11,7 +11,7 @@ ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths this->append(paths); } -ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other) +ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionEntityCollection &other) { this->entities = other.entities; for (size_t i = 0; i < this->entities.size(); ++i) @@ -175,20 +175,20 @@ size_t ExtrusionEntityCollection::items_count() const } // Returns a single vector of pointers to all non-collection items contained in this one. -void ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const -{ - for (const ExtrusionEntity *entity : this->entities) - if (entity->is_collection()) - retval->append(static_cast(entity)->flatten().entities); - else - retval->append(*entity); -} - ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const { - ExtrusionEntityCollection coll; - this->flatten(&coll); - return coll; + struct Flatten { + ExtrusionEntityCollection out; + void recursive_do(const ExtrusionEntityCollection &collection) { + for (const ExtrusionEntity* entity : collection.entities) + if (entity->is_collection()) + this->recursive_do(*static_cast(entity)); + else + out.append(*entity); + } + } flatten; + flatten.recursive_do(*this); + return flatten.out; } double ExtrusionEntityCollection::min_mm3_per_mm() const diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 4fe964ee1..221afc453 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -85,7 +85,6 @@ public: Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } size_t items_count() const; - void flatten(ExtrusionEntityCollection* retval) const; ExtrusionEntityCollection flatten() const; double min_mm3_per_mm() const; double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cbbbf1f1a..4d8482743 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1236,7 +1236,8 @@ std::string Print::validate() const // The comparison of the profiles is not just about element-wise equality, some layers may not be // explicitely included. Always remember z and height of last reference layer that in the vector - // and compare to that. + // and compare to that. In case some layers are in the vectors multiple times, only the last entry is + // taken into account and compared. size_t i = 0; // index into tested profile size_t j = 0; // index into reference profile coordf_t ref_z = -1.; @@ -1244,8 +1245,12 @@ std::string Print::validate() const coordf_t ref_height = -1.; while (i < layer_height_profile.size()) { coordf_t this_z = layer_height_profile[i]; + // find the last entry with this z + while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z) + i += 2; + coordf_t this_height = layer_height_profile[i+1]; - if (next_ref_z < this_z + EPSILON) { + if (ref_height < -1. || next_ref_z < this_z + EPSILON) { ref_z = next_ref_z; do { // one layer can be in the vector several times ref_height = layer_height_profile_tallest[j+1]; diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index 836a0a4d3..c89e4895e 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -42,7 +42,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")"; text += "
"; // End of row header. - text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "
"; + text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "
"; text += _(L("print")) + ": " + snapshot.print + "
"; text += _(L("filaments")) + ": " + snapshot.filaments.front() + "
"; text += _(L("printer")) + ": " + snapshot.printer + "
"; @@ -50,9 +50,9 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve bool compatible = true; for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) { text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() + - ", " + _(L("min slic3r version")) + ": " + vc.version.min_slic3r_version.to_string(); + ", " + _(L("min PrusaSlicer version")) + ": " + vc.version.min_slic3r_version.to_string(); if (vc.version.max_slic3r_version != Semver::inf()) - text += ", " + _(L("max slic3r version")) + ": " + vc.version.max_slic3r_version.to_string(); + text += ", " + _(L("max PrusaSlicer version")) + ": " + vc.version.max_slic3r_version.to_string(); text += "
"; for (const std::pair> &model : vc.models_variants_installed) { text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": "; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 60f76db3d..7a94b8238 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1094,7 +1094,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); @@ -3054,15 +3054,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) wxGetApp().obj_manipul()->set_dirty(); // forces a frame render to update the view before the context menu is shown render(); - - Vec2d logical_pos = pos.cast(); -#if ENABLE_RETINA_GL - const float factor = m_retina_helper->get_scale_factor(); - logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor)); -#endif // ENABLE_RETINA_GL - post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos)); } } + Vec2d logical_pos = pos.cast(); +#if ENABLE_RETINA_GL + const float factor = m_retina_helper->get_scale_factor(); + logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor)); +#endif // ENABLE_RETINA_GL + if (!m_mouse.dragging) + // do not post the event if the user is panning the scene + post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() })); } mouse_up_cleanup(); @@ -3414,7 +3415,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, false), cnv_size.get_width(), cnv_size.get_height()); + m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); m_dirty = true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b15402a52..fb767360c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -71,6 +71,8 @@ public: wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); using Vec2dEvent = Event; +// _bool_ value is used as a indicator of selection in the 3DScene +using RBtnEvent = Event>; template using Vec2dsEvent = ArrayEvent; using Vec3dEvent = Event; @@ -78,7 +80,7 @@ template using Vec3dsEvent = ArrayEvent; wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 9053cdd42..d209214ae 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -284,6 +284,9 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent, wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER) { this->SetFont(wxGetApp().normal_font()); + + // Reset m_enter_pressed flag to _false_, when value is editing + this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId()); this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&) { @@ -307,7 +310,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent, if (!m_enter_pressed) { #ifndef __WXGTK__ /* Update data for next editor selection. - * But under GTK it lucks like there is no information about selected control at e.GetWindow(), + * But under GTK it looks like there is no information about selected control at e.GetWindow(), * so we'll take it from wxEVT_LEFT_DOWN event * */ LayerRangeEditor* new_editor = dynamic_cast(e.GetWindow()); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 2ccdfbc42..9c59b58be 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -255,21 +255,30 @@ void ObjectList::create_objects_ctrl() EnableDropTarget(wxDF_UNICODETEXT); #endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE + const int em = wxGetApp().em_unit(); + // column ItemName(Icon+Text) of the view control: // And Icon can be consisting of several bitmaps AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(), - colName, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); + colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); // column PrintableProperty (Icon) of the view control: - AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, int(2 * wxGetApp().em_unit()), + AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, 3*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // column Extruder of the view control: AppendColumn(create_objects_list_extruder_column(4)); // column ItemEditing of the view control: - AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/, + AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); + + if (wxOSX) + { + GetColumn(colName)->SetWidth(20*em); + GetColumn(colPrint)->SetWidth(3*em); + GetColumn(colExtruder)->SetWidth(8*em); + } } void ObjectList::create_popup_menus() @@ -279,6 +288,7 @@ void ObjectList::create_popup_menus() create_part_popupmenu(&m_menu_part); create_sla_object_popupmenu(&m_menu_sla_object); create_instance_popupmenu(&m_menu_instance); + create_default_popupmenu(&m_menu_default); } void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(nullptr)*/) @@ -783,18 +793,34 @@ void ObjectList::OnChar(wxKeyEvent& event) void ObjectList::OnContextMenu(wxDataViewEvent&) { - list_manipulation(); + list_manipulation(true); } -void ObjectList::list_manipulation() +void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) { wxDataViewItem item; wxDataViewColumn* col = nullptr; const wxPoint pt = get_mouse_position_in_control(); HitTest(pt, item, col); - if (!item || col == nullptr) { - return; + /* Note: Under OSX right click doesn't send "selection changed" event. + * It means that Selection() will be return still previously selected item. + * Thus under OSX we should force UnselectAll(), when item and col are nullptr, + * and select new item otherwise. + */ + + if (!item) { + if (wxOSX && col == nullptr) + UnselectAll(); + if (evt_context_menu) { + show_context_menu(evt_context_menu); + return; + } + } + + if (wxOSX && item && col) { + UnselectAll(); + Select(item); } const wxString title = col->GetTitle(); @@ -802,15 +828,21 @@ void ObjectList::list_manipulation() if (title == " ") toggle_printable_state(item); else if (title == _("Editing")) - show_context_menu(); + show_context_menu(evt_context_menu); else if (title == _("Name")) { - int obj_idx, vol_idx; - get_selected_item_indexes(obj_idx, vol_idx, item); + if (wxOSX) + show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909) - if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0 && - pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() ) - fix_through_netfabb(); + if (is_windows10()) + { + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx, item); + + if (get_mesh_errors_count(obj_idx, vol_idx) > 0 && + pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() ) + fix_through_netfabb(); + } } #ifndef __WXMSW__ @@ -818,7 +850,7 @@ void ObjectList::list_manipulation() #endif //__WXMSW__ } -void ObjectList::show_context_menu() +void ObjectList::show_context_menu(const bool evt_context_menu) { if (multiple_selection()) { @@ -831,22 +863,26 @@ void ObjectList::show_context_menu() } const auto item = GetSelection(); + wxMenu* menu {nullptr}; if (item) { const ItemType type = m_objects_model->GetItemType(item); if (!(type & (itObject | itVolume | itLayer | itInstance))) return; - wxMenu* menu = type & itInstance ? &m_menu_instance : + menu = type & itInstance ? &m_menu_instance : type & itLayer ? &m_menu_layer : m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part : printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object; if (!(type & itInstance)) append_menu_item_settings(menu); - - wxGetApp().plater()->PopupMenu(menu); } + else if (evt_context_menu) + menu = &m_menu_default; + + if (menu) + wxGetApp().plater()->PopupMenu(menu); } void ObjectList::copy() @@ -1286,13 +1322,16 @@ void ObjectList::show_settings(const wxDataViewItem settings_item) wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) { auto sub_menu = new wxMenu; - if (wxGetApp().get_mode() == comExpert) { + if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) { append_menu_item(sub_menu, wxID_ANY, _(L("Load")) + " " + dots, "", [this, type](wxCommandEvent&) { load_subobject(type); }, "", menu); sub_menu->AppendSeparator(); } - for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) { + for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) + { + if (type == ModelVolumeType::INVALID && item == "Slab") + continue; append_menu_item(sub_menu, wxID_ANY, _(item), "", [this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu); } @@ -1600,6 +1639,12 @@ void ObjectList::create_instance_popupmenu(wxMenu*menu) }, m_menu_item_split_instances->GetId()); } +void ObjectList::create_default_popupmenu(wxMenu*menu) +{ + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::INVALID); + append_submenu(menu, sub_menu, wxID_ANY, _(L("Add Shape")), "", "add_part"); +} + wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) { wxMenu *menu = new wxMenu; @@ -1738,8 +1783,38 @@ void ObjectList::load_part( ModelObject* model_object, } +static TriangleMesh create_mesh(const std::string& type_name, const BoundingBoxf3& bb) +{ + TriangleMesh mesh; + + const double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1); + + if (type_name == "Box") + // Sitting on the print bed, left front front corner at (0, 0). + mesh = make_cube(side, side, side); + else if (type_name == "Cylinder") + // Centered around 0, sitting on the print bed. + // The cylinder has the same volume as the box above. + mesh = make_cylinder(0.564 * side, side); + else if (type_name == "Sphere") + // Centered around 0, half the sphere below the print bed, half above. + // The sphere has the same volume as the box above. + mesh = make_sphere(0.62 * side, PI / 18); + else if (type_name == "Slab") + // Sitting on the print bed, left front front corner at (0, 0). + mesh = make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5); + mesh.repair(); + + return mesh; +} + void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type) { + if (type == ModelVolumeType::INVALID) { + load_shape_object(type_name); + return; + } + const int obj_idx = get_selected_obj_idx(); if (obj_idx < 0) return; @@ -1762,26 +1837,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode // Bounding box of the selected instance in world coordinate system including the translation, without modifiers. BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx); - const wxString name = _(L("Generic")) + "-" + _(type_name); - TriangleMesh mesh; - - double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1); - - if (type_name == "Box") - // Sitting on the print bed, left front front corner at (0, 0). - mesh = make_cube(side, side, side); - else if (type_name == "Cylinder") - // Centered around 0, sitting on the print bed. - // The cylinder has the same volume as the box above. - mesh = make_cylinder(0.564 * side, side); - else if (type_name == "Sphere") - // Centered around 0, half the sphere below the print bed, half above. - // The sphere has the same volume as the box above. - mesh = make_sphere(0.62 * side, PI / 18); - else if (type_name == "Slab") - // Sitting on the print bed, left front front corner at (0, 0). - mesh = make_cube(instance_bb.size().x()*1.5, instance_bb.size().y()*1.5, instance_bb.size().z()*0.5); - mesh.repair(); + TriangleMesh mesh = create_mesh(type_name, instance_bb); // Mesh will be centered when loading. ModelVolume *new_volume = model_object.add_volume(std::move(mesh)); @@ -1803,6 +1859,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset); } + const wxString name = _(L("Generic")) + "-" + _(type_name); new_volume->name = into_u8(name); // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -1820,6 +1877,57 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode #endif //no __WXOSX__ //__WXMSW__ } +void ObjectList::load_shape_object(const std::string& type_name) +{ + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + assert(selection.get_object_idx() == -1); // Add nothing is something is selected on 3DScene + if (selection.get_object_idx() != -1) + return; + + const int obj_idx = m_objects->size(); + if (obj_idx < 0) + return; + + take_snapshot(_(L("Add Shape"))); + + // Create mesh + BoundingBoxf3 bb; + TriangleMesh mesh = create_mesh(type_name, bb); + + // Add mesh to model as a new object + Model& model = wxGetApp().plater()->model(); + const wxString name = _(L("Shape")) + "-" + _(type_name); + +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ + + std::vector object_idxs; + ModelObject* new_object = model.add_object(); + new_object->name = into_u8(name); + new_object->add_instance(); // each object should have at list one instance + + ModelVolume* new_volume = new_object->add_volume(mesh); + new_volume->name = into_u8(name); + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + new_object->invalidate_bounding_box(); + + const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); + new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -new_object->origin_translation(2))); + + object_idxs.push_back(model.objects.size() - 1); +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ + + paste_objects_into_list(object_idxs); + +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ +} + void ObjectList::del_object(const int obj_idx) { wxGetApp().plater()->delete_object_from_model(obj_idx); @@ -3606,7 +3714,8 @@ void ObjectList::msw_rescale() &m_menu_part, &m_menu_sla_object, &m_menu_instance, - &m_menu_layer }) + &m_menu_layer, + &m_menu_default}) msw_rescale_menu(menu); Layout(); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 87e2e5aa0..c8d89cbe3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -132,6 +132,7 @@ private: MenuWithSeparators m_menu_sla_object; MenuWithSeparators m_menu_instance; MenuWithSeparators m_menu_layer; + MenuWithSeparators m_menu_default; wxMenuItem* m_menu_item_settings { nullptr }; wxMenuItem* m_menu_item_split_instances { nullptr }; @@ -208,7 +209,7 @@ public: void set_tooltip_for_item(const wxPoint& pt); void selection_changed(); - void show_context_menu(); + void show_context_menu(const bool evt_context_menu); #ifndef __WXOSX__ void key_event(wxKeyEvent& event); #endif /* __WXOSX__ */ @@ -243,6 +244,7 @@ public: void create_sla_object_popupmenu(wxMenu*menu); void create_part_popupmenu(wxMenu*menu); void create_instance_popupmenu(wxMenu*menu); + void create_default_popupmenu(wxMenu *menu); wxMenu* create_settings_popupmenu(wxMenu *parent_menu); void create_freq_settings_popupmenu(wxMenu *parent_menu, const bool is_object_settings = true); @@ -251,6 +253,7 @@ public: void load_subobject(ModelVolumeType type); void load_part(ModelObject* model_object, std::vector> &volumes_info, ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); + void load_shape_object(const std::string &type_name); void del_object(const int obj_idx); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); @@ -365,7 +368,7 @@ private: // void OnChar(wxKeyEvent& event); #endif /* __WXOSX__ */ void OnContextMenu(wxDataViewEvent &event); - void list_manipulation(); + void list_manipulation(bool evt_context_menu = false); void OnBeginDrag(wxDataViewEvent &event); void OnDropPossible(wxDataViewEvent &event); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4db8d6858..2819bcdf2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1343,6 +1343,8 @@ struct Plater::priv MenuWithSeparators part_menu; // SLA-Object popup menu MenuWithSeparators sla_object_menu; + // Default popup menu (when nothing is selected on 3DScene) + MenuWithSeparators default_menu; // Removed/Prepended Items according to the view mode std::vector items_increase; @@ -1882,7 +1884,7 @@ struct Plater::priv void on_action_layersediting(SimpleEvent&); void on_object_select(SimpleEvent&); - void on_right_click(Vec2dEvent&); + void on_right_click(RBtnEvent&); void on_wipetower_moved(Vec3dEvent&); void on_wipetower_rotated(Vec3dEvent&); void on_update_geometry(Vec3dsEvent<2>&); @@ -2530,6 +2532,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) if (output_file.empty()) // Find the file name of the first printable object. output_file = this->model.propose_export_file_name_and_path(); + + if (output_file.empty() && !model.objects.empty()) + // Find the file name of the first object. + output_file = this->model.objects[0]->get_export_filename(); } wxString dlg_title; @@ -3562,57 +3568,66 @@ void Plater::priv::on_object_select(SimpleEvent& evt) selection_changed(); } -void Plater::priv::on_right_click(Vec2dEvent& evt) +void Plater::priv::on_right_click(RBtnEvent& evt) { int obj_idx = get_selected_object_idx(); + + wxMenu* menu = nullptr; + if (obj_idx == -1) - return; - - wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu : - get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject - &object_menu : &part_menu; - - sidebar->obj_list()->append_menu_item_settings(menu); - - if (printer_technology != ptSLA) - sidebar->obj_list()->append_menu_item_change_extruder(menu); - - if (menu != &part_menu) + menu = &default_menu; + else { - /* Remove/Prepend "increase/decrease instances" menu items according to the view mode. - * Suppress to show those items for a Simple mode - */ - const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF; - if (wxGetApp().get_mode() == comSimple) { - if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND) - { - /* Detach an items from the menu, but don't delete them - * so that they can be added back later - * (after switching to the Advanced/Expert mode) - */ - menu->Remove(items_increase[id]); - menu->Remove(items_decrease[id]); - menu->Remove(items_set_number_of_copies[id]); + // If in 3DScene is(are) selected volume(s), but right button was clicked on empty space + if (evt.data.second) + return; + + menu = printer_technology == ptSLA ? &sla_object_menu : + get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject + &object_menu : &part_menu; + + sidebar->obj_list()->append_menu_item_settings(menu); + + if (printer_technology != ptSLA) + sidebar->obj_list()->append_menu_item_change_extruder(menu); + + if (menu != &part_menu) + { + /* Remove/Prepend "increase/decrease instances" menu items according to the view mode. + * Suppress to show those items for a Simple mode + */ + const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF; + if (wxGetApp().get_mode() == comSimple) { + if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND) + { + /* Detach an items from the menu, but don't delete them + * so that they can be added back later + * (after switching to the Advanced/Expert mode) + */ + menu->Remove(items_increase[id]); + menu->Remove(items_decrease[id]); + menu->Remove(items_set_number_of_copies[id]); + } } - } - else { - if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND) - { - // Prepend items to the menu, if those aren't not there - menu->Prepend(items_set_number_of_copies[id]); - menu->Prepend(items_decrease[id]); - menu->Prepend(items_increase[id]); + else { + if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND) + { + // Prepend items to the menu, if those aren't not there + menu->Prepend(items_set_number_of_copies[id]); + menu->Prepend(items_decrease[id]); + menu->Prepend(items_increase[id]); + } } } } - if (q != nullptr) { + if (q != nullptr && menu) { #ifdef __linux__ // For some reason on Linux the menu isn't displayed if position is specified // (even though the position is sane). q->PopupMenu(menu); #else - q->PopupMenu(menu, (int)evt.data.x(), (int)evt.data.y()); + q->PopupMenu(menu, (int)evt.data.first.x(), (int)evt.data.first.y()); #endif } } @@ -3664,12 +3679,14 @@ bool Plater::priv::init_object_menu() init_common_menu(&part_menu, true); complit_init_part_menu(); + sidebar->obj_list()->create_default_popupmenu(&default_menu); + return true; } void Plater::priv::msw_rescale_object_menu() { - for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu }) + for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu }) msw_rescale_menu(dynamic_cast(menu)); } @@ -4493,14 +4510,14 @@ void Plater::set_number_of_copies(/*size_t num*/) ModelObject* model_object = p->model.objects[obj_idx]; - const auto num = wxGetNumberFromUser( " ", _("Enter the number of copies:"), + const int num = wxGetNumberFromUser( " ", _("Enter the number of copies:"), _("Copies of the selected object"), model_object->instances.size(), 0, 1000, this ); if (num < 0) return; Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num)); - int diff = (int)num - (int)model_object->instances.size(); + int diff = num - (int)model_object->instances.size(); if (diff > 0) increase_instances(diff); else if (diff < 0) @@ -5058,6 +5075,11 @@ GLCanvas3D* Plater::canvas3D() return p->view3D->get_canvas3d(); } +BoundingBoxf Plater::bed_shape_bb() const +{ + return p->bed_shape_bb(); +} + PrinterTechnology Plater::printer_technology() const { return p->printer_technology; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f23259e47..6ebecac44 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -228,6 +228,7 @@ public: int get_selected_object_idx(); bool is_single_full_object_selection() const; GLCanvas3D* canvas3D(); + BoundingBoxf bed_shape_bb() const; PrinterTechnology printer_technology() const; void set_printer_technology(PrinterTechnology printer_technology); diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index a868ea6bb..1f16ae3d4 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -31,13 +31,11 @@ ExtrusionEntityCollection* flatten() %code{% RETVAL = new ExtrusionEntityCollection(); - THIS->flatten(RETVAL); + *RETVAL = THIS->flatten(); %}; double min_mm3_per_mm(); bool empty() %code{% RETVAL = THIS->entities.empty(); %}; - std::vector orig_indices() - %code{% RETVAL = THIS->orig_indices; %}; Polygons polygons_covered_by_width(); Polygons polygons_covered_by_spacing(); %{