Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_reload_from_disk

This commit is contained in:
Enrico Turri 2019-09-23 08:27:01 +02:00
commit c0576a8770
18 changed files with 342 additions and 159 deletions

View file

@ -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); memcpy(buffer, &stl->stats.number_of_facets, 4);
stl_internal_reverse_quads(buffer, 4); stl_internal_reverse_quads(buffer, 4);
fwrite(buffer, 4, 1, fp); fwrite(buffer, 4, 1, fp);
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { for (const stl_facet &facet : stl->facet_start) {
memcpy(buffer, stl->facet_start + i, 50); memcpy(buffer, &facet, 50);
// Convert to little endian. // Convert to little endian.
stl_internal_reverse_quads(buffer, 48); stl_internal_reverse_quads(buffer, 48);
fwrite(buffer, SIZEOF_STL_FACET, 1, fp); fwrite(buffer, SIZEOF_STL_FACET, 1, fp);

View file

@ -1,3 +1,12 @@
2018-01-17 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
(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 <j.gnu@uriah.heep.sax.de> 2016-05-10 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
Submitted by Hannes Jochriem: Submitted by Hannes Jochriem:

View file

@ -505,12 +505,19 @@ network connection to (TCP)
on on
.Ar host .Ar host
is established. 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 The remote endpoint is assumed to be a terminal or console server
that connects the network stream to a local serial port where the that connects the network stream to a local serial port where the
actual programmer has been attached to. actual programmer has been attached to.
The port is assumed to be properly configured, for example using a The port is assumed to be properly configured, for example using a
transparent 8-bit data connection without parity at 115200 Baud transparent 8-bit data connection without parity at 115200 Baud
for a STK500. for a STK500.
.Pp
Note: The ability to handle IPv6 hostnames and addresses is limited to
Posix systems (by now).
.It Fl q .It Fl q
Disable (or quell) output of the progress bar while reading or writing Disable (or quell) output of the progress bar while reading or writing
to the device. Specify it a second time for even quieter operation. to the device. Specify it a second time for even quieter operation.

View file

@ -214,7 +214,7 @@ AC_HEADER_TIME
AC_CHECK_LIB([ws2_32], [puts]) AC_CHECK_LIB([ws2_32], [puts])
# Checks for library functions. # 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]) AC_MSG_CHECKING([for a Win32 HID libray])
SAVED_LIBS="${LIBS}" SAVED_LIBS="${LIBS}"

View file

@ -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 In this case, instead of trying to open a local device, a TCP
network connection to (TCP) @var{port} on @var{host} network connection to (TCP) @var{port} on @var{host}
is established. 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 The remote endpoint is assumed to be a terminal or console server
that connects the network stream to a local serial port where the that connects the network stream to a local serial port where the
actual programmer has been attached to. 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 transparent 8-bit data connection without parity at 115200 Baud
for a STK500. for a STK500.
Note: The ability to handle IPv6 hostnames and addresses is limited to
Posix systems (by now).
@item -q @item -q
Disable (or quell) output of the progress bar while reading or writing Disable (or quell) output of the progress bar while reading or writing

View file

@ -150,6 +150,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
return 0; return 0;
} }
#include "ac_cfg.h"
// Timeout read & write variants // Timeout read & write variants
// Additionally to the regular -1 on I/O error, they return -2 on timeout // 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 static int
net_open(const char *port, union filedescriptor *fdp) net_open(const char *port, union filedescriptor *fdp)
{ {
char *hstr, *pstr, *end; #ifdef HAVE_GETADDRINFO
unsigned int pnum; char *hp, *hstr, *pstr;
int fd; int s, fd, ret = -1;
struct sockaddr_in sockaddr; struct addrinfo hints;
struct hostent *hp; 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", avrdude_message(MSG_INFO, "%s: net_open(): Out of memory!\n",
progname); progname);
return -1; 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", avrdude_message(MSG_INFO, "%s: net_open(): Mangled host:port string \"%s\"\n",
progname, hstr); progname, hstr);
free(hstr); goto error;
return -1; }
/*
* 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'; *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)) { if (s != 0) {
avrdude_message(MSG_INFO, "%s: net_open(): Bad port number \"%s\"\n", avrdude_message(MSG_INFO,
progname, pstr); "%s: net_open(): Cannot resolve "
free(hstr); "host=\"%s\", port=\"%s\": %s\n",
return -1; progname, hstr, pstr, gai_strerror(s));
goto error;
} }
for (rp = result; rp != NULL; rp = rp->ai_next) {
if ((hp = gethostbyname(hstr)) == NULL) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
avrdude_message(MSG_INFO, "%s: net_open(): unknown host \"%s\"\n", if (fd == -1) {
progname, hstr); /* This one failed, loop over */
free(hstr); continue;
return -1; }
if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
/* Success, we are connected */
break;
}
close(fd);
} }
if (rp == NULL) {
free(hstr); avrdude_message(MSG_INFO, "%s: net_open(): Cannot connect: %s\n",
progname, strerror(errno));
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;
} }
else {
memset(&sockaddr, 0, sizeof(struct sockaddr_in)); fdp->ifd = fd;
sockaddr.sin_family = AF_INET; ret = 0;
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;
} }
freeaddrinfo(result);
fdp->ifd = fd; error:
return 0; 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 */
} }

View file

@ -11,7 +11,7 @@ ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths
this->append(paths); this->append(paths);
} }
ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other) ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionEntityCollection &other)
{ {
this->entities = other.entities; this->entities = other.entities;
for (size_t i = 0; i < this->entities.size(); ++i) 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. // 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<const ExtrusionEntityCollection*>(entity)->flatten().entities);
else
retval->append(*entity);
}
ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const
{ {
ExtrusionEntityCollection coll; struct Flatten {
this->flatten(&coll); ExtrusionEntityCollection out;
return coll; void recursive_do(const ExtrusionEntityCollection &collection) {
for (const ExtrusionEntity* entity : collection.entities)
if (entity->is_collection())
this->recursive_do(*static_cast<const ExtrusionEntityCollection*>(entity));
else
out.append(*entity);
}
} flatten;
flatten.recursive_do(*this);
return flatten.out;
} }
double ExtrusionEntityCollection::min_mm3_per_mm() const double ExtrusionEntityCollection::min_mm3_per_mm() const

View file

@ -85,7 +85,6 @@ public:
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
{ Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
size_t items_count() const; size_t items_count() const;
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const; ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() 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; } double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }

View file

@ -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 // 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 // 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 i = 0; // index into tested profile
size_t j = 0; // index into reference profile size_t j = 0; // index into reference profile
coordf_t ref_z = -1.; coordf_t ref_z = -1.;
@ -1244,8 +1245,12 @@ std::string Print::validate() const
coordf_t ref_height = -1.; coordf_t ref_height = -1.;
while (i < layer_height_profile.size()) { while (i < layer_height_profile.size()) {
coordf_t this_z = layer_height_profile[i]; 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]; 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; ref_z = next_ref_z;
do { // one layer can be in the vector several times do { // one layer can be in the vector several times
ref_height = layer_height_profile_tallest[j+1]; ref_height = layer_height_profile_tallest[j+1];

View file

@ -42,7 +42,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")"; text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")";
text += "</b></font><br>"; text += "</b></font><br>";
// End of row header. // End of row header.
text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>"; text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
text += _(L("print")) + ": " + snapshot.print + "<br>"; text += _(L("print")) + ": " + snapshot.print + "<br>";
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>"; text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
text += _(L("printer")) + ": " + snapshot.printer + "<br>"; text += _(L("printer")) + ": " + snapshot.printer + "<br>";
@ -50,9 +50,9 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
bool compatible = true; bool compatible = true;
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) { for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() + 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()) 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 += "<br>"; text += "<br>";
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) { for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": "; text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";

View file

@ -1094,7 +1094,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, 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_REMOVE_OBJECT, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
@ -3054,15 +3054,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
wxGetApp().obj_manipul()->set_dirty(); wxGetApp().obj_manipul()->set_dirty();
// forces a frame render to update the view before the context menu is shown // forces a frame render to update the view before the context menu is shown
render(); render();
Vec2d logical_pos = pos.cast<double>();
#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<double>();
#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(); mouse_up_cleanup();
@ -3414,7 +3415,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
void GLCanvas3D::set_camera_zoom(double zoom) void GLCanvas3D::set_camera_zoom(double zoom)
{ {
const Size& cnv_size = get_canvas_size(); 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; m_dirty = true;
} }

View file

@ -71,6 +71,8 @@ public:
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
using Vec2dEvent = Event<Vec2d>; using Vec2dEvent = Event<Vec2d>;
// _bool_ value is used as a indicator of selection in the 3DScene
using RBtnEvent = Event<std::pair<Vec2d, bool>>;
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>; template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
using Vec3dEvent = Event<Vec3d>; using Vec3dEvent = Event<Vec3d>;
@ -78,7 +80,7 @@ template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, 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_REMOVE_OBJECT, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);

View file

@ -284,6 +284,9 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER) wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
{ {
this->SetFont(wxGetApp().normal_font()); 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&) this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
{ {
@ -307,7 +310,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
if (!m_enter_pressed) { if (!m_enter_pressed) {
#ifndef __WXGTK__ #ifndef __WXGTK__
/* Update data for next editor selection. /* 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 * so we'll take it from wxEVT_LEFT_DOWN event
* */ * */
LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow()); LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());

View file

@ -255,21 +255,30 @@ void ObjectList::create_objects_ctrl()
EnableDropTarget(wxDF_UNICODETEXT); EnableDropTarget(wxDF_UNICODETEXT);
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE #endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
const int em = wxGetApp().em_unit();
// column ItemName(Icon+Text) of the view control: // column ItemName(Icon+Text) of the view control:
// And Icon can be consisting of several bitmaps // And Icon can be consisting of several bitmaps
AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(), 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: // 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); wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
// column Extruder of the view control: // column Extruder of the view control:
AppendColumn(create_objects_list_extruder_column(4)); AppendColumn(create_objects_list_extruder_column(4));
// column ItemEditing of the view control: // 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); 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() void ObjectList::create_popup_menus()
@ -279,6 +288,7 @@ void ObjectList::create_popup_menus()
create_part_popupmenu(&m_menu_part); create_part_popupmenu(&m_menu_part);
create_sla_object_popupmenu(&m_menu_sla_object); create_sla_object_popupmenu(&m_menu_sla_object);
create_instance_popupmenu(&m_menu_instance); 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)*/) 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&) void ObjectList::OnContextMenu(wxDataViewEvent&)
{ {
list_manipulation(); list_manipulation(true);
} }
void ObjectList::list_manipulation() void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
{ {
wxDataViewItem item; wxDataViewItem item;
wxDataViewColumn* col = nullptr; wxDataViewColumn* col = nullptr;
const wxPoint pt = get_mouse_position_in_control(); const wxPoint pt = get_mouse_position_in_control();
HitTest(pt, item, col); HitTest(pt, item, col);
if (!item || col == nullptr) { /* Note: Under OSX right click doesn't send "selection changed" event.
return; * 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(); const wxString title = col->GetTitle();
@ -802,15 +828,21 @@ void ObjectList::list_manipulation()
if (title == " ") if (title == " ")
toggle_printable_state(item); toggle_printable_state(item);
else if (title == _("Editing")) else if (title == _("Editing"))
show_context_menu(); show_context_menu(evt_context_menu);
else if (title == _("Name")) else if (title == _("Name"))
{ {
int obj_idx, vol_idx; if (wxOSX)
get_selected_item_indexes(obj_idx, vol_idx, item); 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 && if (is_windows10())
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() ) {
fix_through_netfabb(); 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__ #ifndef __WXMSW__
@ -818,7 +850,7 @@ void ObjectList::list_manipulation()
#endif //__WXMSW__ #endif //__WXMSW__
} }
void ObjectList::show_context_menu() void ObjectList::show_context_menu(const bool evt_context_menu)
{ {
if (multiple_selection()) if (multiple_selection())
{ {
@ -831,22 +863,26 @@ void ObjectList::show_context_menu()
} }
const auto item = GetSelection(); const auto item = GetSelection();
wxMenu* menu {nullptr};
if (item) if (item)
{ {
const ItemType type = m_objects_model->GetItemType(item); const ItemType type = m_objects_model->GetItemType(item);
if (!(type & (itObject | itVolume | itLayer | itInstance))) if (!(type & (itObject | itVolume | itLayer | itInstance)))
return; return;
wxMenu* menu = type & itInstance ? &m_menu_instance : menu = type & itInstance ? &m_menu_instance :
type & itLayer ? &m_menu_layer : type & itLayer ? &m_menu_layer :
m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part : m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part :
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object; printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
if (!(type & itInstance)) if (!(type & itInstance))
append_menu_item_settings(menu); 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() 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) { wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) {
auto sub_menu = new wxMenu; 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, "", append_menu_item(sub_menu, wxID_ANY, _(L("Load")) + " " + dots, "",
[this, type](wxCommandEvent&) { load_subobject(type); }, "", menu); [this, type](wxCommandEvent&) { load_subobject(type); }, "", menu);
sub_menu->AppendSeparator(); 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), "", append_menu_item(sub_menu, wxID_ANY, _(item), "",
[this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu); [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()); }, 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* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
{ {
wxMenu *menu = new wxMenu; 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) 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(); const int obj_idx = get_selected_obj_idx();
if (obj_idx < 0) if (obj_idx < 0)
return; 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. // 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); BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx);
const wxString name = _(L("Generic")) + "-" + _(type_name); TriangleMesh mesh = create_mesh(type_name, instance_bb);
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();
// Mesh will be centered when loading. // Mesh will be centered when loading.
ModelVolume *new_volume = model_object.add_volume(std::move(mesh)); 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); 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); new_volume->name = into_u8(name);
// set a default extruder value, since user can't add it manually // set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); 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__ #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<size_t> 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<double>(), -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) void ObjectList::del_object(const int obj_idx)
{ {
wxGetApp().plater()->delete_object_from_model(obj_idx); wxGetApp().plater()->delete_object_from_model(obj_idx);
@ -3606,7 +3714,8 @@ void ObjectList::msw_rescale()
&m_menu_part, &m_menu_part,
&m_menu_sla_object, &m_menu_sla_object,
&m_menu_instance, &m_menu_instance,
&m_menu_layer }) &m_menu_layer,
&m_menu_default})
msw_rescale_menu(menu); msw_rescale_menu(menu);
Layout(); Layout();

View file

@ -132,6 +132,7 @@ private:
MenuWithSeparators m_menu_sla_object; MenuWithSeparators m_menu_sla_object;
MenuWithSeparators m_menu_instance; MenuWithSeparators m_menu_instance;
MenuWithSeparators m_menu_layer; MenuWithSeparators m_menu_layer;
MenuWithSeparators m_menu_default;
wxMenuItem* m_menu_item_settings { nullptr }; wxMenuItem* m_menu_item_settings { nullptr };
wxMenuItem* m_menu_item_split_instances { nullptr }; wxMenuItem* m_menu_item_split_instances { nullptr };
@ -208,7 +209,7 @@ public:
void set_tooltip_for_item(const wxPoint& pt); void set_tooltip_for_item(const wxPoint& pt);
void selection_changed(); void selection_changed();
void show_context_menu(); void show_context_menu(const bool evt_context_menu);
#ifndef __WXOSX__ #ifndef __WXOSX__
void key_event(wxKeyEvent& event); void key_event(wxKeyEvent& event);
#endif /* __WXOSX__ */ #endif /* __WXOSX__ */
@ -243,6 +244,7 @@ public:
void create_sla_object_popupmenu(wxMenu*menu); void create_sla_object_popupmenu(wxMenu*menu);
void create_part_popupmenu(wxMenu*menu); void create_part_popupmenu(wxMenu*menu);
void create_instance_popupmenu(wxMenu*menu); void create_instance_popupmenu(wxMenu*menu);
void create_default_popupmenu(wxMenu *menu);
wxMenu* create_settings_popupmenu(wxMenu *parent_menu); wxMenu* create_settings_popupmenu(wxMenu *parent_menu);
void create_freq_settings_popupmenu(wxMenu *parent_menu, const bool is_object_settings = true); 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_subobject(ModelVolumeType type);
void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type); void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
void load_generic_subobject(const std::string& type_name, const 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_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item); void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item); void del_settings_from_config(const wxDataViewItem& parent_item);
@ -365,7 +368,7 @@ private:
// void OnChar(wxKeyEvent& event); // void OnChar(wxKeyEvent& event);
#endif /* __WXOSX__ */ #endif /* __WXOSX__ */
void OnContextMenu(wxDataViewEvent &event); void OnContextMenu(wxDataViewEvent &event);
void list_manipulation(); void list_manipulation(bool evt_context_menu = false);
void OnBeginDrag(wxDataViewEvent &event); void OnBeginDrag(wxDataViewEvent &event);
void OnDropPossible(wxDataViewEvent &event); void OnDropPossible(wxDataViewEvent &event);

View file

@ -1343,6 +1343,8 @@ struct Plater::priv
MenuWithSeparators part_menu; MenuWithSeparators part_menu;
// SLA-Object popup menu // SLA-Object popup menu
MenuWithSeparators sla_object_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 // Removed/Prepended Items according to the view mode
std::vector<wxMenuItem*> items_increase; std::vector<wxMenuItem*> items_increase;
@ -1882,7 +1884,7 @@ struct Plater::priv
void on_action_layersediting(SimpleEvent&); void on_action_layersediting(SimpleEvent&);
void on_object_select(SimpleEvent&); void on_object_select(SimpleEvent&);
void on_right_click(Vec2dEvent&); void on_right_click(RBtnEvent&);
void on_wipetower_moved(Vec3dEvent&); void on_wipetower_moved(Vec3dEvent&);
void on_wipetower_rotated(Vec3dEvent&); void on_wipetower_rotated(Vec3dEvent&);
void on_update_geometry(Vec3dsEvent<2>&); void on_update_geometry(Vec3dsEvent<2>&);
@ -2530,6 +2532,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
if (output_file.empty()) if (output_file.empty())
// Find the file name of the first printable object. // Find the file name of the first printable object.
output_file = this->model.propose_export_file_name_and_path(); 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; wxString dlg_title;
@ -3562,57 +3568,66 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
selection_changed(); 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(); int obj_idx = get_selected_object_idx();
wxMenu* menu = nullptr;
if (obj_idx == -1) if (obj_idx == -1)
return; menu = &default_menu;
else
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)
{ {
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode. // If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
* Suppress to show those items for a Simple mode if (evt.data.second)
*/ return;
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
if (wxGetApp().get_mode() == comSimple) { menu = printer_technology == ptSLA ? &sla_object_menu :
if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND) get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
{ &object_menu : &part_menu;
/* Detach an items from the menu, but don't delete them
* so that they can be added back later sidebar->obj_list()->append_menu_item_settings(menu);
* (after switching to the Advanced/Expert mode)
*/ if (printer_technology != ptSLA)
menu->Remove(items_increase[id]); sidebar->obj_list()->append_menu_item_change_extruder(menu);
menu->Remove(items_decrease[id]);
menu->Remove(items_set_number_of_copies[id]); 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 {
else { if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND) {
{ // Prepend items to the menu, if those aren't not there
// Prepend items to the menu, if those aren't not there menu->Prepend(items_set_number_of_copies[id]);
menu->Prepend(items_set_number_of_copies[id]); menu->Prepend(items_decrease[id]);
menu->Prepend(items_decrease[id]); menu->Prepend(items_increase[id]);
menu->Prepend(items_increase[id]); }
} }
} }
} }
if (q != nullptr) { if (q != nullptr && menu) {
#ifdef __linux__ #ifdef __linux__
// For some reason on Linux the menu isn't displayed if position is specified // For some reason on Linux the menu isn't displayed if position is specified
// (even though the position is sane). // (even though the position is sane).
q->PopupMenu(menu); q->PopupMenu(menu);
#else #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 #endif
} }
} }
@ -3664,12 +3679,14 @@ bool Plater::priv::init_object_menu()
init_common_menu(&part_menu, true); init_common_menu(&part_menu, true);
complit_init_part_menu(); complit_init_part_menu();
sidebar->obj_list()->create_default_popupmenu(&default_menu);
return true; return true;
} }
void Plater::priv::msw_rescale_object_menu() 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<wxMenu*>(menu)); msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
} }
@ -4493,14 +4510,14 @@ void Plater::set_number_of_copies(/*size_t num*/)
ModelObject* model_object = p->model.objects[obj_idx]; 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 ); _("Copies of the selected object"), model_object->instances.size(), 0, 1000, this );
if (num < 0) if (num < 0)
return; return;
Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num)); 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) if (diff > 0)
increase_instances(diff); increase_instances(diff);
else if (diff < 0) else if (diff < 0)
@ -5058,6 +5075,11 @@ GLCanvas3D* Plater::canvas3D()
return p->view3D->get_canvas3d(); return p->view3D->get_canvas3d();
} }
BoundingBoxf Plater::bed_shape_bb() const
{
return p->bed_shape_bb();
}
PrinterTechnology Plater::printer_technology() const PrinterTechnology Plater::printer_technology() const
{ {
return p->printer_technology; return p->printer_technology;

View file

@ -228,6 +228,7 @@ public:
int get_selected_object_idx(); int get_selected_object_idx();
bool is_single_full_object_selection() const; bool is_single_full_object_selection() const;
GLCanvas3D* canvas3D(); GLCanvas3D* canvas3D();
BoundingBoxf bed_shape_bb() const;
PrinterTechnology printer_technology() const; PrinterTechnology printer_technology() const;
void set_printer_technology(PrinterTechnology printer_technology); void set_printer_technology(PrinterTechnology printer_technology);

View file

@ -31,13 +31,11 @@
ExtrusionEntityCollection* flatten() ExtrusionEntityCollection* flatten()
%code{% %code{%
RETVAL = new ExtrusionEntityCollection(); RETVAL = new ExtrusionEntityCollection();
THIS->flatten(RETVAL); *RETVAL = THIS->flatten();
%}; %};
double min_mm3_per_mm(); double min_mm3_per_mm();
bool empty() bool empty()
%code{% RETVAL = THIS->entities.empty(); %}; %code{% RETVAL = THIS->entities.empty(); %};
std::vector<size_t> orig_indices()
%code{% RETVAL = THIS->orig_indices; %};
Polygons polygons_covered_by_width(); Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing(); Polygons polygons_covered_by_spacing();
%{ %{