From 9146ef2f6140d183c18523b07368b6b7710e5a60 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 1 Jun 2020 11:11:38 +0200 Subject: [PATCH 01/12] Copy/Paste for the Settings and Layers in the ObjectList --- src/slic3r/GUI/GUI_ObjectList.cpp | 108 +++++++++++++++++++++++++----- src/slic3r/GUI/GUI_ObjectList.hpp | 31 ++++++++- src/slic3r/GUI/Plater.cpp | 14 +++- 3 files changed, 131 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 441f30816..b8f596e6b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -695,33 +695,41 @@ void ObjectList::selection_changed() part_selection_changed(); } -void ObjectList::fill_layer_config_ranges_cache() +void ObjectList::copy_layers_to_clipboard() { wxDataViewItemArray sel_layers; GetSelections(sel_layers); - const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers[0]); + const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers.front()); if (obj_idx < 0 || (int)m_objects->size() <= obj_idx) return; const t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; - m_layer_config_ranges_cache.clear(); + t_layer_config_ranges& cache_ranges = m_clipboard.get_ranges_cache(); + + if (sel_layers.Count() == 1 && m_objects_model->GetItemType(sel_layers.front()) & itLayerRoot) + { + cache_ranges.clear(); + cache_ranges = ranges; + return; + } for (const auto layer_item : sel_layers) if (m_objects_model->GetItemType(layer_item) & itLayer) { auto range = m_objects_model->GetLayerRangeByItem(layer_item); auto it = ranges.find(range); if (it != ranges.end()) - m_layer_config_ranges_cache[it->first] = it->second; + cache_ranges[it->first] = it->second; } } void ObjectList::paste_layers_into_list() { const int obj_idx = m_objects_model->GetObjectIdByItem(GetSelection()); + t_layer_config_ranges& cache_ranges = m_clipboard.get_ranges_cache(); if (obj_idx < 0 || (int)m_objects->size() <= obj_idx || - m_layer_config_ranges_cache.empty() || printer_technology() == ptSLA) + cache_ranges.empty() || printer_technology() == ptSLA) return; const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx); @@ -732,7 +740,7 @@ void ObjectList::paste_layers_into_list() t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; // and create Layer item(s) according to the layer_config_ranges - for (const auto range : m_layer_config_ranges_cache) + for (const auto range : cache_ranges) ranges.emplace(range); layers_item = add_layer_root_item(object_item); @@ -745,6 +753,48 @@ void ObjectList::paste_layers_into_list() #endif //no __WXOSX__ } +void ObjectList::copy_settings_to_clipboard() +{ + wxDataViewItem item = GetSelection(); + assert(item.IsOk()); + if (m_objects_model->GetItemType(item) & itSettings) + item = m_objects_model->GetParent(item); + + DynamicPrintConfig& config_cache = m_clipboard.get_config_cache(); + config_cache = get_item_config(item); +} + +void ObjectList::paste_settings_into_list() +{ + wxDataViewItem item = GetSelection(); + assert(item.IsOk()); + if (m_objects_model->GetItemType(item) & itSettings) + item = m_objects_model->GetParent(item); + + ItemType item_type = m_objects_model->GetItemType(item); + if(!(item_type & (itObject | itVolume |itLayer))) + return; + + DynamicPrintConfig& config_cache = m_clipboard.get_config_cache(); + assert(!config_cache.empty()); + + auto keys = config_cache.keys(); + auto part_options = get_options(true); + + for (const std::string& opt_key: keys) { + if (item_type & (itVolume | itLayer) && + std::find(part_options.begin(), part_options.end(), opt_key) == part_options.end()) + continue; // we can't to add object specific options for the part's(itVolume | itLayer) config + + const ConfigOption* option = config_cache.option(opt_key); + if (option) + m_config->set_key_value(opt_key, option->clone()); + } + + // Add settings item for object/sub-object and show them + show_settings(add_settings_item(item, m_config)); +} + void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes) { if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx)) @@ -984,20 +1034,46 @@ void ObjectList::extruder_editing() void ObjectList::copy() { - // if (m_selection_mode & smLayer) - // fill_layer_config_ranges_cache(); - // else { - // m_layer_config_ranges_cache.clear(); - wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); - // } + wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); } void ObjectList::paste() { - // if (!m_layer_config_ranges_cache.empty()) - // paste_layers_into_list(); - // else - wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); + wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); +} + +bool ObjectList::copy_to_clipboard() +{ + wxDataViewItemArray sels; + GetSelections(sels); + ItemType type = m_objects_model->GetItemType(sels.front()); + if (!(type & (itSettings | itLayer | itLayerRoot))) { + m_clipboard.reset(); + return false; + } + + if (type & itSettings) + copy_settings_to_clipboard(); + if (type & (itLayer | itLayerRoot)) + copy_layers_to_clipboard(); + + m_clipboard.set_type(type); + return true; +} + +bool ObjectList::paste_from_clipboard() +{ + if (!(m_clipboard.get_type() & (itSettings | itLayer | itLayerRoot))) { + m_clipboard.reset(); + return false; + } + + if (m_clipboard.get_type() & itSettings) + paste_settings_into_list(); + if (m_clipboard.get_type() & (itLayer | itLayerRoot)) + paste_layers_into_list(); + + return true; } void ObjectList::undo() diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 4490ec5e9..ad222a4a8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -81,10 +81,32 @@ public: smLayerRoot = 16, // used for undo/redo }; + struct Clipboard + { + void reset() { + m_type = itUndef; + m_layer_config_ranges_cache .clear(); + m_config_cache.clear(); + } + bool empty() const { return m_type == itUndef; } + ItemType get_type() const { return m_type; } + void set_type(ItemType type) { m_type = type; } + + t_layer_config_ranges& get_ranges_cache() { return m_layer_config_ranges_cache; } + DynamicPrintConfig& get_config_cache() { return m_config_cache; } + + private: + ItemType m_type {itUndef}; + t_layer_config_ranges m_layer_config_ranges_cache; + DynamicPrintConfig m_config_cache; + }; + private: SELECTION_MODE m_selection_mode {smUndef}; int m_selected_layers_range_idx; + Clipboard m_clipboard; + struct dragged_item_data { void init(const int obj_idx, const int subobj_idx, const ItemType type) { @@ -148,8 +170,6 @@ private: std::vector m_bmp_vector; - t_layer_config_ranges m_layer_config_ranges_cache; - int m_selected_object_id = -1; bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select() // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler @@ -230,6 +250,8 @@ public: void copy(); void paste(); + bool copy_to_clipboard(); + bool paste_from_clipboard(); void undo(); void redo(); @@ -385,8 +407,11 @@ public: void fix_through_netfabb(); void update_item_error_icon(const int obj_idx, int vol_idx) const ; - void fill_layer_config_ranges_cache(); + void copy_layers_to_clipboard(); void paste_layers_into_list(); + void copy_settings_to_clipboard(); + void paste_settings_into_list(); + bool clipboard_is_empty() const { return m_clipboard.empty(); } void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes); void paste_objects_into_list(const std::vector& object_idxs); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 03bbab796..34b151a93 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5456,7 +5456,10 @@ void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_acti void Plater::copy_selection_to_clipboard() { - if (can_copy_to_clipboard()) + // At first try to copy selected values to the ObjectList's clipboard + // to check if Settings or Layers are selected in the list + // and then copy to 3DCanvas's clipboard if not + if (can_copy_to_clipboard() && !p->sidebar->obj_list()->copy_to_clipboard()) p->view3D->get_canvas3d()->get_selection().copy_to_clipboard(); } @@ -5466,7 +5469,12 @@ void Plater::paste_from_clipboard() return; Plater::TakeSnapshot snapshot(this, _L("Paste From Clipboard")); - p->view3D->get_canvas3d()->get_selection().paste_from_clipboard(); + + // At first try to paste values from the ObjectList's clipboard + // to check if Settings or Layers were copied + // and then paste from the 3DCanvas's clipboard if not + if (!p->sidebar->obj_list()->paste_from_clipboard()) + p->view3D->get_canvas3d()->get_selection().paste_from_clipboard(); } void Plater::search(bool plater_is_active) @@ -5591,7 +5599,7 @@ bool Plater::can_paste_from_clipboard() const const Selection& selection = p->view3D->get_canvas3d()->get_selection(); const Selection::Clipboard& clipboard = selection.get_clipboard(); - if (clipboard.is_empty()) + if (clipboard.is_empty() && p->sidebar->obj_list()->clipboard_is_empty()) return false; if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !clipboard.is_sla_compliant()) From 4be0e37963aeb03819402f93bc9d9106535c5772 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 6 May 2020 16:44:52 +0200 Subject: [PATCH 02/12] Workaround for items out of bed after arrange. Fixes #4329 --- .../libnest2d/backends/clipper/geometries.hpp | 6 +++--- .../include/libnest2d/placers/nfpplacer.hpp | 15 ++++++--------- src/libslic3r/Arrange.cpp | 7 +++++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 57da6ec12..dd80322bc 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -77,7 +77,7 @@ inline void offset(PolygonImpl& sh, TCoord distance, const PolygonTag #define DISABLE_BOOST_OFFSET using ClipperLib::ClipperOffset; - using ClipperLib::jtMiter; + using ClipperLib::jtSquare; using ClipperLib::etClosedPolygon; using ClipperLib::Paths; @@ -85,8 +85,8 @@ inline void offset(PolygonImpl& sh, TCoord distance, const PolygonTag try { ClipperOffset offs; - offs.AddPath(sh.Contour, jtMiter, etClosedPolygon); - offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon); + offs.AddPath(sh.Contour, jtSquare, etClosedPolygon); + offs.AddPaths(sh.Holes, jtSquare, etClosedPolygon); offs.Execute(result, static_cast(distance)); } catch (ClipperLib::clipperException &) { throw GeometryException(GeomErr::OFFSET); diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 91f04cb93..6cdaadd25 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -528,15 +528,12 @@ public: static inline double overfit(const Box& bb, const Box& bin) { - auto Bw = bin.width(); - auto Bh = bin.height(); - auto mBw = -Bw; - auto mBh = -Bh; - auto wdiff = double(bb.width()) + mBw; - auto hdiff = double(bb.height()) + mBh; - double diff = 0; - if(wdiff > 0) diff += wdiff; - if(hdiff > 0) diff += hdiff; + auto wdiff = TCompute(bb.width()) - bin.width(); + auto hdiff = TCompute(bb.height()) - bin.height(); + double diff = .0; + if(wdiff > 0) diff += double(wdiff); + if(hdiff > 0) diff += double(hdiff); + return diff; } diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index de6b34c1f..6ae7dd6a2 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -396,11 +396,14 @@ template<> std::function AutoArranger::get_objfn() double score = std::get<0>(result); auto& fullbb = std::get<1>(result); - double miss = Placer::overfit(fullbb, m_bin); + auto bin = m_bin; + sl::offset(bin, -EPSILON * (m_bin.width() + m_bin.height())); + + double miss = Placer::overfit(fullbb, bin); miss = miss > 0? miss : 0; score += miss*miss; - return score; + return score; }; } From aa92cbf051b56c7174079d8d1cfc42c2440847ad Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 2 Jun 2020 10:02:50 +0200 Subject: [PATCH 03/12] New tech ENABLE_SMOOTH_NORMALS (disabled) -> Added two experimental functions to smooth normals using libigl (none of them working properly in detecting edges) when calling GLIndexedVertexArray::load_mesh_full_shading() --- src/libslic3r/Technologies.hpp | 3 + src/slic3r/GUI/3DScene.cpp | 118 ++++++++++++++++++++++++++++++--- src/slic3r/GUI/3DScene.hpp | 7 +- src/slic3r/GUI/GLCanvas3D.cpp | 16 ++++- 4 files changed, 131 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index b29b1a7f6..0c5720c6b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -45,5 +45,8 @@ // Enable rendering of objects using environment map #define ENABLE_ENVIRONMENT_MAP (1 && ENABLE_2_3_0_ALPHA1) +// Enable smoothing of objects normals +#define ENABLE_SMOOTH_NORMALS (0 && ENABLE_2_3_0_ALPHA1) + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 4c61270cf..dc57500c4 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1,5 +1,11 @@ #include +#if ENABLE_SMOOTH_NORMALS +#include +#include +#include +#endif // ENABLE_SMOOTH_NORMALS + #include "3DScene.hpp" #if ENABLE_ENVIRONMENT_MAP #include "GUI_App.hpp" @@ -57,23 +63,107 @@ void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char namespace Slic3r { -void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) +#if ENABLE_SMOOTH_NORMALS +static void smooth_normals_corner(TriangleMesh& mesh, std::vector& normals) +{ + mesh.repair(); + + using MapMatrixXfUnaligned = Eigen::Map>; + using MapMatrixXiUnaligned = Eigen::Map>; + + std::vector face_normals(mesh.stl.stats.number_of_facets); + for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + face_normals[i] = mesh.stl.facet_start[i].normal; + } + + Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), 3).cast(); + Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), 3); + Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), + Eigen::Index(face_normals.size()), 3).cast(); + Eigen::MatrixXd out_normals; + + igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); + + normals = std::vector(mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast(); + } + } +} + +static void smooth_normals_vertex(TriangleMesh& mesh, std::vector& normals) +{ + mesh.repair(); + + using MapMatrixXfUnaligned = Eigen::Map>; + using MapMatrixXiUnaligned = Eigen::Map>; + + Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), 3).cast(); + Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), 3); + Eigen::MatrixXd out_normals; + +// igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM, out_normals); +// igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA, out_normals); + igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE, out_normals); +// igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT, out_normals); + + normals = std::vector(mesh.its.vertices.size()); + for (size_t i = 0; i < static_cast(out_normals.rows()); ++i) { + normals[i] = out_normals.row(i).cast(); + } +} +#endif // ENABLE_SMOOTH_NORMALS + +#if ENABLE_SMOOTH_NORMALS +void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals) +#else +void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh) +#endif // ENABLE_SMOOTH_NORMALS { assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); assert(quad_indices.empty() && triangle_indices_size == 0); assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); - this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); +#if ENABLE_SMOOTH_NORMALS + if (smooth_normals) { + TriangleMesh new_mesh(mesh); + std::vector normals; + smooth_normals_corner(new_mesh, normals); +// smooth_normals_vertex(new_mesh, normals); - unsigned int vertices_count = 0; - for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) { - const stl_facet &facet = mesh.stl.facet_start[i]; - for (int j = 0; j < 3; ++j) - this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); + this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 2 * new_mesh.its.vertices.size()); + for (size_t i = 0; i < new_mesh.its.vertices.size(); ++i) { + const stl_vertex& v = new_mesh.its.vertices[i]; + const stl_normal& n = normals[i]; + this->push_geometry(v(0), v(1), v(2), n(0), n(1), n(2)); + } - this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); - vertices_count += 3; + for (size_t i = 0; i < new_mesh.its.indices.size(); ++i) { + const stl_triangle_vertex_indices& idx = new_mesh.its.indices[i]; + this->push_triangle(idx(0), idx(1), idx(2)); + } } + else { +#endif // ENABLE_SMOOTH_NORMALS + this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); + + unsigned int vertices_count = 0; + for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) { + const stl_facet& facet = mesh.stl.facet_start[i]; + for (int j = 0; j < 3; ++j) + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); + + this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); + vertices_count += 3; + } +#if ENABLE_SMOOTH_NORMALS + } +#endif // ENABLE_SMOOTH_NORMALS } void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized) @@ -462,7 +552,11 @@ int GLVolumeCollection::load_object_volume( this->volumes.emplace_back(new GLVolume(color)); GLVolume& v = *this->volumes.back(); v.set_color_from_model_volume(model_volume); +#if ENABLE_SMOOTH_NORMALS + v.indexed_vertex_array.load_mesh(mesh, true); +#else v.indexed_vertex_array.load_mesh(mesh); +#endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); if (model_volume->is_model_part()) @@ -504,8 +598,12 @@ void GLVolumeCollection::load_object_auxiliary( const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); GLVolume& v = *this->volumes.back(); +#if ENABLE_SMOOTH_NORMALS + v.indexed_vertex_array.load_mesh(mesh, true); +#else v.indexed_vertex_array.load_mesh(mesh); - v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // ENABLE_SMOOTH_NORMALS + v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.geometry_id = std::pair(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 1401e79fb..da111d7aa 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -125,8 +125,13 @@ public: unsigned int triangle_indices_VBO_id{ 0 }; unsigned int quad_indices_VBO_id{ 0 }; - void load_mesh_full_shading(const TriangleMesh &mesh); +#if ENABLE_SMOOTH_NORMALS + void load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals = false); + void load_mesh(const TriangleMesh& mesh, bool smooth_normals = false) { this->load_mesh_full_shading(mesh, smooth_normals); } +#else + void load_mesh_full_shading(const TriangleMesh& mesh); void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); } +#endif // ENABLE_SMOOTH_NORMALS inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 69bebc301..9c4a8d2b3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2491,11 +2491,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); assert(! mesh.empty()); mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); +#if ENABLE_SMOOTH_NORMALS + volume.indexed_vertex_array.load_mesh(mesh, true); +#else volume.indexed_vertex_array.load_mesh(mesh); - } else { +#endif // ENABLE_SMOOTH_NORMALS + } else { // Reload the original volume. +#if ENABLE_SMOOTH_NORMALS + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#else volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); - } +#endif // ENABLE_SMOOTH_NORMALS + } volume.finalize_geometry(true); } //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable @@ -6814,7 +6822,11 @@ void GLCanvas3D::_load_sla_shells() const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) { m_volumes.volumes.emplace_back(new GLVolume(color)); GLVolume& v = *m_volumes.volumes.back(); +#if ENABLE_SMOOTH_NORMALS + v.indexed_vertex_array.load_mesh(mesh, true); +#else v.indexed_vertex_array.load_mesh(mesh); +#endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.finalize_geometry(this->m_initialized); v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.composite_id.volume_id = volume_id; From a4d30fc9bb6e4fb9cc29550052f9ae98761d86d0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 2 Jun 2020 11:42:22 +0200 Subject: [PATCH 04/12] Fixed a crash when switching between tabs Introduced in ba963ac --- src/slic3r/GUI/MainFrame.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d35b397a6..c1a59b909 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -325,21 +325,22 @@ void MainFrame::init_tabpanel() } m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) { - auto panel = m_tabpanel->GetCurrentPage(); + wxWindow* panel = m_tabpanel->GetCurrentPage(); + Tab* tab = dynamic_cast(panel); // There shouldn't be a case, when we try to select a tab, which doesn't support a printer technology - if (panel == nullptr || !static_cast(panel)->supports_printer_technology(m_plater->printer_technology())) + if (panel == nullptr || (tab && tab->supports_printer_technology(m_plater->printer_technology()))) return; auto& tabs_list = wxGetApp().tabs_list; - if (find(tabs_list.begin(), tabs_list.end(), panel) != tabs_list.end()) { + if (tab && std::find(tabs_list.begin(), tabs_list.end(), tab) != tabs_list.end()) { // On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered // before the MainFrame is fully set up. - static_cast(panel)->OnActivate(); + tab->OnActivate(); m_last_selected_tab = m_tabpanel->GetSelection(); } else - select_tab(0); + select_tab(0); // select Plater }); if (m_layout == slOld) { From dc725ecbad034678aa3ff8e6a72821757b931513 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 2 Jun 2020 14:49:40 +0200 Subject: [PATCH 05/12] Fixup of previous commit --- src/slic3r/GUI/MainFrame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index c1a59b909..cc599387d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -329,7 +329,7 @@ void MainFrame::init_tabpanel() Tab* tab = dynamic_cast(panel); // There shouldn't be a case, when we try to select a tab, which doesn't support a printer technology - if (panel == nullptr || (tab && tab->supports_printer_technology(m_plater->printer_technology()))) + if (panel == nullptr || (tab && ! tab->supports_printer_technology(m_plater->printer_technology()))) return; auto& tabs_list = wxGetApp().tabs_list; From 10c59b0d00a63e729d8913fdb7550911afda4055 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Jun 2020 17:15:08 +0200 Subject: [PATCH 06/12] Fix the tests after EigenMesh3D refactor --- src/libslic3r/SLA/EigenMesh3D.hpp | 2 +- tests/sla_print/sla_raycast_tests.cpp | 3 ++- tests/sla_print/sla_test_utils.cpp | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index fd60c04ed..b932c0c18 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -7,7 +7,7 @@ // There is an implementation of a hole-aware raycaster that was eventually // not used in production version. It is now hidden under following define // for possible future use. -#define SLIC3R_HOLE_RAYCASTER +// #define SLIC3R_HOLE_RAYCASTER #ifdef SLIC3R_HOLE_RAYCASTER #include "libslic3r/SLA/Hollowing.hpp" diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp index 74c799472..c82e4569a 100644 --- a/tests/sla_print/sla_raycast_tests.cpp +++ b/tests/sla_print/sla_raycast_tests.cpp @@ -39,7 +39,7 @@ TEST_CASE("Raycaster - find intersections of a line and cylinder") REQUIRE(std::abs(out[1].first - std::sqrt(72.f)) < 0.001f); } - +#ifdef SLIC3R_HOLE_RAYCASTER // Create a simple scene with a 20mm cube and a big hole in the front wall // with 5mm radius. Then shoot rays from interesting positions and see where // they land. @@ -94,3 +94,4 @@ TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]") // Check for support tree correctness test_support_model_collision("20mm_cube.obj", {}, hcfg, holes); } +#endif diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 883e4268a..1eaf796c0 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -105,8 +105,13 @@ void test_supports(const std::string &obj_filename, // Create the special index-triangle mesh with spatial indexing which // is the input of the support point and support mesh generators sla::EigenMesh3D emesh{mesh}; + +#ifdef SLIC3R_HOLE_RAYCASTER if (hollowingcfg.enabled) emesh.load_holes(drainholes); +#endif + + // TODO: do the cgal hole cutting... // Create the support point generator sla::SupportPointGenerator::Config autogencfg; From 2cc1dffc824d31936bb199921aefe7964c0d0577 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 1 Jun 2020 10:01:45 +0200 Subject: [PATCH 07/12] Fix of several issues related to gizmos updating and undo/redo Common gizmos data cannot be used in on_set_state method Also prevented calling render on empty GLVertexArrays --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 56 +++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 3 ++ src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 6 +-- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 7 +-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 27 +++++----- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 13 +++-- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 5 -- src/slic3r/GUI/MeshUtils.cpp | 3 +- 8 files changed, 70 insertions(+), 50 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index da5695397..cd4285724 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -54,18 +54,35 @@ bool GLGizmoFdmSupports::on_init() return true; } + +void GLGizmoFdmSupports::activate_internal_undo_redo_stack(bool activate) +{ + if (activate && ! m_internal_stack_active) { + Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned on")); + wxGetApp().plater()->enter_gizmos_stack(); + m_internal_stack_active = true; + } + if (! activate && m_internal_stack_active) { + wxGetApp().plater()->leave_gizmos_stack(); + Plater::TakeSnapshot(wxGetApp().plater(), _L("FDM gizmo turned off")); + m_internal_stack_active = false; + } +} + void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const Selection& selection) { - const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; - if (! mo) + if (m_state != On) return; + const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; + if (mo && selection.is_from_single_instance() - && (mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size)) + && (m_schedule_update || mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size)) { update_from_model_object(); m_old_mo_id = mo->id(); m_old_volumes_size = mo->volumes.size(); + m_schedule_update = false; } } @@ -131,8 +148,10 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const // Now render both enforcers and blockers. for (int i=0; i<2; ++i) { glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f)); - for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i]) - iva.render(); + for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i]) { + if (iva.has_VBOs()) + iva.render(); + } } glsafe(::glPopMatrix()); if (is_left_handed) @@ -493,6 +512,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous : (m_button_down == Button::Left ? _L("Add supports") : _L("Block supports")); + activate_internal_undo_redo_stack(true); Plater::TakeSnapshot(wxGetApp().plater(), action_name); update_model_object(); @@ -588,6 +608,8 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwr update_vertex_buffers(&mv->mesh(), mesh_id, FacetSupportType::BLOCKER); } + activate_internal_undo_redo_stack(true); + Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") : _L("Add supports by angle")); update_model_object(); @@ -778,12 +800,9 @@ void GLGizmoFdmSupports::on_set_state() return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on"))); - } if (! m_parent.get_gizmos_manager().is_serializing()) { - wxGetApp().CallAfter([]() { - wxGetApp().plater()->enter_gizmos_stack(); + wxGetApp().CallAfter([this]() { + activate_internal_undo_redo_stack(true); }); } } @@ -793,11 +812,7 @@ void GLGizmoFdmSupports::on_set_state() m_setting_angle = false; m_parent.use_slope(false); } - - wxGetApp().plater()->leave_gizmos_stack(); - { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off"))); - } + activate_internal_undo_redo_stack(false); m_old_mo_id = -1; m_ivas.clear(); m_selected_facets.clear(); @@ -820,14 +835,19 @@ void GLGizmoFdmSupports::on_stop_dragging() -void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive& ar) +void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive&) { - update_from_model_object(); + // We should update the gizmo from current ModelObject, but it is not + // possible at this point. That would require having updated selection and + // common gizmos data, which is not done at this point. Instead, save + // a flag to do the update in set_fdm_support_data, which will be called + // soon after. + m_schedule_update = true; } -void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive& ar) const +void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const { } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index d765a8da5..c4f5b153e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -64,6 +64,7 @@ private: void update_model_object() const; void update_from_model_object(); + void activate_internal_undo_redo_stack(bool activate); void select_facets_by_angle(float threshold, bool overwrite, bool block); bool m_overwrite_selected = false; @@ -74,6 +75,8 @@ private: float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; bool m_setting_angle = false; + bool m_internal_stack_active = false; + bool m_schedule_update = false; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 262b685a6..c9e8b9d2b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -28,8 +28,7 @@ bool GLGizmoFlatten::on_init() void GLGizmoFlatten::on_set_state() { - if (m_state == On && is_plane_update_necessary()) - update_planes(); + } CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const @@ -81,7 +80,8 @@ void GLGizmoFlatten::on_render() const else glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.5f)); - m_planes[i].vbo.render(); + if (m_planes[i].vbo.has_VBOs()) + m_planes[i].vbo.render(); } glsafe(::glPopMatrix()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 1dffee6be..658db64ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -59,7 +59,7 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) return; const ModelObject* mo = m_c->selection_info()->model_object(); - if (mo) { + if (m_state == On && mo) { if (m_old_mo_id != mo->id()) { reload_cache(); m_old_mo_id = mo->id(); @@ -810,11 +810,6 @@ void GLGizmoHollow::on_set_state() if (m_state == m_old_state) return; - if (m_state == On && m_old_state != On) { // the gizmo was just turned on - // we'll now reload support points: - if (m_c->selection_info()->model_object()) - reload_cache(); - } if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); m_old_state = m_state; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 268a15df3..908fe27b1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -67,10 +67,11 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S ModelObject* mo = m_c->selection_info()->model_object(); - if (mo && mo->id() != m_old_mo_id) { + if (m_state == On && mo && mo->id() != m_old_mo_id) { disable_editing_mode(); reload_cache(); m_old_mo_id = mo->id(); + m_c->instances_hider()->show_supports(true); } // If we triggered autogeneration before, check backend and fetch results if they are there @@ -884,25 +885,23 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const void GLGizmoSlaSupports::on_set_state() { - const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state == m_old_state) return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); + // This function can be called from undo/redo, when selection (and hence + // common gizmos data are not yet deserialized. The CallAfter should put + // this off until after the update is done. + wxGetApp().CallAfter([this]() { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); - // we'll now reload support points: - if (mo) - reload_cache(); - - // Set default head diameter from config. - const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; - m_c->instances_hider()->show_supports(true); + // Set default head diameter from config. + const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; + }); } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = mo && m_editing_mode && unsaved_changes(); + bool will_ask = m_editing_mode && unsaved_changes(); if (will_ask) { wxGetApp().CallAfter([this]() { // Following is called through CallAfter, because otherwise there was a problem @@ -922,7 +921,7 @@ void GLGizmoSlaSupports::on_set_state() disable_editing_mode(); // so it is not active next time the gizmo opens Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_normal_cache.clear(); - + m_old_mo_id = -1; } } m_old_state = m_state; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 355927fb1..511c68735 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -1104,9 +1104,16 @@ void GLGizmosManager::activate_gizmo(EType type) } m_current = type; - m_common_gizmos_data->update(get_current() - ? get_current()->get_requirements() - : CommonGizmosDataID(0)); + + // Updating common data should be left to the update_data function, which + // is always called after this one. activate_gizmo can be called by undo/redo, + // when selection is not yet deserialized, so the common data would update + // incorrectly (or crash if relying on unempty selection). Undo/redo stack + // will also call update_data, after selection is restored. + + //m_common_gizmos_data->update(get_current() + // ? get_current()->get_requirements() + // : CommonGizmosDataID(0)); if (type != Undefined) m_gizmos[type]->set_state(GLGizmoBase::On); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index e643c0b3b..4ad46a2a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -141,11 +141,6 @@ public: EType new_current = m_current; m_current = old_current; - // Update common data. They should be updated when activate_gizmo is - // called, so it can be used in on_set_state which is called from there. - if (new_current != Undefined) - m_common_gizmos_data->update(m_gizmos[new_current]->get_requirements()); - // activate_gizmo call sets m_current and calls set_state for the gizmo // it does nothing in case the gizmo is already activated // it can safely be called for Undefined gizmo diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 413eedda5..581f50a88 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -49,7 +49,8 @@ void MeshClipper::render_cut() if (! m_triangles_valid) recalculate_triangles(); - m_vertex_array.render(); + if (m_vertex_array.has_VBOs()) + m_vertex_array.render(); } From 8ecb0bfe0e009dddd87175d6ae0980ca68bbc0cf Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 2 Jun 2020 16:08:18 +0200 Subject: [PATCH 08/12] Revert "Workaround for the Prusa3D Fast (layer height 0.35mm) profile, which" This reverts commit ec86d94f026cd304f2544fcf6990cf360f9db235. --- src/libslic3r/GCode/ToolOrdering.cpp | 67 +++++++--------------------- src/libslic3r/GCode/ToolOrdering.hpp | 2 +- 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 011420e71..aa3eddbb8 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -94,7 +94,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height, object.config().layer_height); + this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height); this->collect_extruder_statistics(prime_multi_material); } @@ -107,7 +107,6 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Initialize the print layers for all objects and all layers. coordf_t object_bottom_z = 0.; - coordf_t max_layer_height = 0.; { std::vector zs; for (auto object : print.objects()) { @@ -123,8 +122,6 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool object_bottom_z = layer->print_z - layer->height; break; } - - max_layer_height = std::max(max_layer_height, object->config().layer_height.value); } this->initialize_layers(zs); } @@ -147,7 +144,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height); + this->fill_wipe_tower_partitions(print.config(), object_bottom_z); this->collect_extruder_statistics(prime_multi_material); } @@ -321,7 +318,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) } } -void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_object_layer_height) +void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z) { if (m_layer_tools.empty()) return; @@ -354,10 +351,6 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ mlh = 0.75 * config.nozzle_diameter.values[i]; max_layer_height = std::min(max_layer_height, mlh); } - // The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed - // by the nozzle. This is a hack and it works by increasing extrusion width. - max_layer_height = std::max(max_layer_height, max_object_layer_height); - for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) { const LayerTools < = m_layer_tools[i]; const LayerTools <_next = m_layer_tools[i + 1]; @@ -400,47 +393,21 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ // and maybe other problems. We will therefore go through layer_tools and detect and fix this. // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), // we'll mark it with has_wipe tower. - assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower); - if (! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower) { - for (size_t i = 0; i + 1 < m_layer_tools.size();) { - const LayerTools < = m_layer_tools[i]; - assert(lt.has_wipe_tower); - assert(! lt.extruders.empty()); - // Find the next layer with wipe tower or mark a layer as such. - size_t j = i + 1; - for (; j < m_layer_tools.size() && ! m_layer_tools[j].has_wipe_tower; ++ j) { - LayerTools <_next = m_layer_tools[j]; - if (lt_next.extruders.empty()) { - //FIXME Vojtech: Lukasi, proc? - j = m_layer_tools.size(); - break; - } - if (lt_next.extruders.front() != lt.extruders.back() || lt_next.extruders.size() > 1) { - // Support only layer, soluble layers? Otherwise the layer should have been already marked as having wipe tower. - assert(lt_next.has_support && ! lt_next.has_object); - lt_next.has_wipe_tower = true; - break; - } + for (unsigned int i=0; i+1 1)) + lt_next.has_wipe_tower = true; + // We should also check that the next wipe tower layer is no further than max_layer_height: + unsigned int j = i+1; + double last_wipe_tower_print_z = lt_next.print_z; + while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower) + if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height) { + m_layer_tools[j].has_wipe_tower = true; + last_wipe_tower_print_z = m_layer_tools[j].print_z; } - if (j == m_layer_tools.size()) - // No wipe tower above layer i, therefore no need to add any wipe tower layer above i. - break; - // We should also check that the next wipe tower layer is no further than max_layer_height. - // This algorith may in theory create very thin wipe layer j if layer closely below j is marked as wipe tower. - // This may happen if printing with non-soluble break away supports. - // On the other side it should not hurt as there will be no wipe, just perimeter and sparse infill printed - // at that particular wipe tower layer without extruder change. - double last_wipe_tower_print_z = lt.print_z; - assert(m_layer_tools[j].has_wipe_tower); - for (size_t k = i + 1; k < j; ++k) { - assert(! m_layer_tools[k].has_wipe_tower); - if (m_layer_tools[k + 1].print_z - last_wipe_tower_print_z > max_layer_height + EPSILON) { - m_layer_tools[k].has_wipe_tower = true; - last_wipe_tower_print_z = m_layer_tools[k].print_z; - } - } - i = j; - } } // Calculate the wipe_tower_layer_height values. diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index e2e07533f..04ef25a41 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -168,7 +168,7 @@ private: void initialize_layers(std::vector &zs); void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); void reorder_extruders(unsigned int last_extruder_id); - void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height); + void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void collect_extruder_statistics(bool prime_multi_material); std::vector m_layer_tools; From ed98a859af7c591a7d13aee9d0a0391c258738e2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 2 Jun 2020 16:29:42 +0200 Subject: [PATCH 09/12] Fix of #3919 (extremely wide wipe tower with 0.35mm FAST profile) This is an excerpt from ec86d94, which was recently reverted because the other changes from that commit broke some functionality. --- src/libslic3r/GCode/ToolOrdering.cpp | 15 +++++++++++---- src/libslic3r/GCode/ToolOrdering.hpp | 2 +- src/slic3r/GUI/GUI_ObjectList.hpp | 2 ++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index aa3eddbb8..43f498b60 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -94,7 +94,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height); + this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height, object.config().layer_height); this->collect_extruder_statistics(prime_multi_material); } @@ -107,6 +107,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Initialize the print layers for all objects and all layers. coordf_t object_bottom_z = 0.; + coordf_t max_layer_height = 0.; { std::vector zs; for (auto object : print.objects()) { @@ -122,6 +123,8 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool object_bottom_z = layer->print_z - layer->height; break; } + + max_layer_height = std::max(max_layer_height, object->config().layer_height.value); } this->initialize_layers(zs); } @@ -144,7 +147,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(print.config(), object_bottom_z); + this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height); this->collect_extruder_statistics(prime_multi_material); } @@ -318,7 +321,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) } } -void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z) +void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_object_layer_height) { if (m_layer_tools.empty()) return; @@ -351,6 +354,10 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ mlh = 0.75 * config.nozzle_diameter.values[i]; max_layer_height = std::min(max_layer_height, mlh); } + // The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed + // by the nozzle. This is a hack and it works by increasing extrusion width. See GH #3919. + max_layer_height = std::max(max_layer_height, max_object_layer_height); + for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) { const LayerTools < = m_layer_tools[i]; const LayerTools <_next = m_layer_tools[i + 1]; @@ -404,7 +411,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ unsigned int j = i+1; double last_wipe_tower_print_z = lt_next.print_z; while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower) - if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height) { + if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height + EPSILON) { m_layer_tools[j].has_wipe_tower = true; last_wipe_tower_print_z = m_layer_tools[j].print_z; } diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 04ef25a41..e2e07533f 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -168,7 +168,7 @@ private: void initialize_layers(std::vector &zs); void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); void reorder_extruders(unsigned int last_extruder_id); - void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); + void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height); void collect_extruder_statistics(bool prime_multi_material); std::vector m_layer_tools; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index ad222a4a8..aa5264b07 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -13,6 +13,8 @@ #include "wxExtensions.hpp" #include "ObjectDataViewModel.hpp" +#include "libslic3r/PrintConfig.hpp" + class wxBoxSizer; class wxBitmapComboBox; class wxMenuItem; From 65295435185966f441d9b27e2464c35bfa591f05 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 3 Jun 2020 14:49:40 +0200 Subject: [PATCH 10/12] WIP: first_layer_print_min/max, first_layer_print_convex_hull placeholders for the G-code export. --- src/libslic3r/GCode.cpp | 16 +++++- src/libslic3r/Layer.hpp | 1 + src/libslic3r/Print.cpp | 107 +++++++++++++++++++++++++++------------- src/libslic3r/Print.hpp | 19 +++++++ 4 files changed, 107 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 51dd9f929..a109a9b15 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1298,7 +1298,21 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). - + { + // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. + // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. + // It does NOT encompass user extrusions generated by custom G-code, + // therefore it does NOT encompass the initial purge line. + // It does NOT encompass MMU/MMU2 starting (wipe) areas. + auto pts = std::make_unique(); + pts->values.reserve(print.first_layer_convex_hull().size()); + for (const Point &pt : print.first_layer_convex_hull().points) + pts->values.emplace_back(unscale(pt)); + BoundingBoxf bbox(pts->values); + m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); + m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + } std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 54e4baf2c..c104d46da 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -166,6 +166,7 @@ class SupportLayer : public Layer { public: // Polygons covered by the supports: base, interface and contact areas. + // Used to suppress retraction if moving for a support extrusion over these support_islands. ExPolygonCollection support_islands; // Extrusion paths for the support base and for the support interface and contacts. ExtrusionEntityCollection support_fills; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index dad41a642..bd04aab15 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -244,7 +244,6 @@ bool Print::invalidate_step(PrintStep step) { bool invalidated = Inherited::invalidate_step(step); // Propagate to dependent steps. - //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? if (step == psSkirt) invalidated |= Inherited::invalidate_step(psBrim); if (step != psGCodeExport) @@ -1606,6 +1605,8 @@ void Print::process() } if (this->set_started(psSkirt)) { m_skirt.clear(); + m_skirt_convex_hull.clear(); + m_first_layer_convex_hull.points.clear(); if (this->has_skirt()) { this->set_status(88, L("Generating skirt")); this->_make_skirt(); @@ -1614,11 +1615,15 @@ void Print::process() } if (this->set_started(psBrim)) { m_brim.clear(); + m_first_layer_convex_hull.points.clear(); if (m_config.brim_width > 0) { this->set_status(88, L("Generating brim")); this->_make_brim(); } - this->set_done(psBrim); + // Brim depends on skirt (brim lines are trimmed by the skirt lines), therefore if + // the skirt gets invalidated, brim gets invalidated as well and the following line is called. + this->finalize_first_layer_convex_hull(); + this->set_done(psBrim); } BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } @@ -1697,22 +1702,7 @@ void Print::_make_skirt() } // Include the wipe tower. - if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { - double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; - double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; - Vec2d pt = Vec2d(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); - - std::vector pts; - pts.push_back(Vec2d(pt.x(), pt.y())); - pts.push_back(Vec2d(pt.x()+width, pt.y())); - pts.push_back(Vec2d(pt.x()+width, pt.y()+depth)); - pts.push_back(Vec2d(pt.x(), pt.y()+depth)); - for (Vec2d& pt : pts) { - pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; - pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); - points.push_back(Point(scale_(pt.x()), scale_(pt.y()))); - } - } + append(points, this->first_layer_wipe_tower_corners()); if (points.size() < 3) // At least three points required for a convex hull. @@ -1796,28 +1786,19 @@ void Print::_make_skirt() } // Brims were generated inside out, reverse to print the outmost contour first. m_skirt.reverse(); + + // Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull. + for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1)))) + append(m_skirt_convex_hull, std::move(poly.points)); } void Print::_make_brim() { // Brim is only printed on first layer and uses perimeter extruder. + Polygons islands = this->first_layer_islands(); + Polygons loops; Flow flow = this->brim_flow(); - Polygons islands; - for (PrintObject *object : m_objects) { - Polygons object_islands; - for (ExPolygon &expoly : object->m_layers.front()->lslices) - object_islands.push_back(expoly.contour); - if (! object->support_layers().empty()) - object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); - islands.reserve(islands.size() + object_islands.size() * object->instances().size()); - for (const PrintInstance &instance : object->instances()) - for (Polygon &poly : object_islands) { - islands.push_back(poly); - islands.back().translate(instance.shift); - } - } - Polygons loops; - size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); + size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); for (size_t i = 0; i < num_loops; ++ i) { this->throw_if_canceled(); islands = offset(islands, float(flow.scaled_spacing()), jtSquare); @@ -1828,6 +1809,11 @@ void Print::_make_brim() p.pop_back(); poly.points = std::move(p); } + if (i + 1 == num_loops) { + // Remember the outer edge of the last brim line extruded as m_first_layer_convex_hull. + for (Polygon &poly : islands) + append(m_first_layer_convex_hull.points, poly.points); + } polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); } loops = union_pt_chained(loops, false); @@ -1967,6 +1953,58 @@ void Print::_make_brim() } } +Polygons Print::first_layer_islands() const +{ + Polygons islands; + for (PrintObject *object : m_objects) { + Polygons object_islands; + for (ExPolygon &expoly : object->m_layers.front()->lslices) + object_islands.push_back(expoly.contour); + if (! object->support_layers().empty()) + object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); + islands.reserve(islands.size() + object_islands.size() * object->instances().size()); + for (const PrintInstance &instance : object->instances()) + for (Polygon &poly : object_islands) { + islands.push_back(poly); + islands.back().translate(instance.shift); + } + } + return islands; +} + +std::vector Print::first_layer_wipe_tower_corners() const +{ + std::vector corners; + if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { + double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; + double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; + Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); + for (Vec2d pt : { + pt0, + Vec2d(pt0.x()+width, pt0.y() ), + Vec2d(pt0.x()+width, pt0.y()+depth), + Vec2d(pt0.x(), pt0.y()+depth) + }) { + pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; + pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); + corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); + } + } + return corners; +} + +void Print::finalize_first_layer_convex_hull() +{ + append(m_first_layer_convex_hull.points, m_skirt_convex_hull); + if (m_first_layer_convex_hull.empty()) { + // Neither skirt nor brim was extruded. Collect points of printed objects from 1st layer. + for (Polygon &poly : this->first_layer_islands()) + append(m_first_layer_convex_hull.points, std::move(poly.points)); + } + append(m_first_layer_convex_hull.points, this->first_layer_wipe_tower_corners()); + m_first_layer_convex_hull = Geometry::convex_hull(m_first_layer_convex_hull.points); +} + // Wipe tower support. bool Print::has_wipe_tower() const { @@ -1991,7 +2029,6 @@ const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_l return m_wipe_tower_data; } - void Print::_make_wipe_tower() { m_wipe_tower_data.clear(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 5fb395fb1..41f8fb041 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -402,6 +402,12 @@ public: const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& brim() const { return m_brim; } + // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. + // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. + // It does NOT encompass user extrusions generated by custom G-code, + // therefore it does NOT encompass the initial purge line. + // It does NOT encompass MMU/MMU2 starting (wipe) areas. + const Polygon& first_layer_convex_hull() const { return m_first_layer_convex_hull; } const PrintStatistics& print_statistics() const { return m_print_statistics; } @@ -437,6 +443,12 @@ private: void _make_skirt(); void _make_brim(); void _make_wipe_tower(); + void finalize_first_layer_convex_hull(); + + // Islands of objects and their supports extruded at the 1st layer. + Polygons first_layer_islands() const; + // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. + std::vector first_layer_wipe_tower_corners() const; // Declared here to have access to Model / ModelObject / ModelInstance static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); @@ -450,6 +462,13 @@ private: // Ordered collections of extrusion paths to build skirt loops and brim. ExtrusionEntityCollection m_skirt; ExtrusionEntityCollection m_brim; + // Convex hull of the 1st layer extrusions. + // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. + // It does NOT encompass user extrusions generated by custom G-code, + // therefore it does NOT encompass the initial purge line. + // It does NOT encompass MMU/MMU2 starting (wipe) areas. + Polygon m_first_layer_convex_hull; + Points m_skirt_convex_hull; // Following section will be consumed by the GCodeGenerator. ToolOrdering m_tool_ordering; From b8267a5f6fca7f1d7f5536f933be2f35dcc4935f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 3 Jun 2020 16:29:42 +0200 Subject: [PATCH 11/12] G-code placeholder parser - new values: print_bed_min, print_bed_max, print_bed_size calculated from a bounding box of bed_shape vector of points. Also added first_layer_print_size as a size of a first layer print bounding box. --- src/libslic3r/GCode.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a109a9b15..284d85ea3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1298,6 +1298,12 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + { + BoundingBoxf bbox(print.config().bed_shape.values); + m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + } { // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. @@ -1310,8 +1316,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu pts->values.emplace_back(unscale(pt)); BoundingBoxf bbox(pts->values); m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); - m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); } std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. From 1e5d1cb616742ef64070ae93d8acf3c722612993 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 3 Jun 2020 16:37:20 +0200 Subject: [PATCH 12/12] Fixed a crash when switching from SLA to FDM with a gizmo active If a gizmo used SupportsClipper or HollowedMesh common data, it would attempt to update from a dead SLAPrintObject --- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 9dd9c6565..051e9cf88 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -8,6 +8,8 @@ #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/PresetBundle.hpp" + #include namespace Slic3r { @@ -170,7 +172,8 @@ void InstancesHider::show_supports(bool show) { void HollowedMesh::on_update() { const ModelObject* mo = get_pool()->selection_info()->model_object(); - if (! mo) + bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; + if (! mo || ! is_sla) return; const GLCanvas3D* canvas = get_pool()->get_canvas(); @@ -376,7 +379,8 @@ void ObjectClipper::set_position(double pos, bool keep_normal) void SupportsClipper::on_update() { const ModelObject* mo = get_pool()->selection_info()->model_object(); - if (! mo) + bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; + if (! mo || ! is_sla) return; const GLCanvas3D* canvas = get_pool()->get_canvas();