diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bcfb8faec..7be2f2098 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1322,11 +1322,11 @@ void GCode::process_layer( // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). bool colorprint_change = false; - while (!m_colorprint_heights.empty() && m_colorprint_heights.front()/*-EPSILON*/ < layer.print_z-EPSILON) { + while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) { m_colorprint_heights.erase(m_colorprint_heights.begin()); colorprint_change = true; } - if (colorprint_change) + if (colorprint_change && print.extruders().size()==1) gcode += "M600\n"; diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 1d14c36e6..3f2df9532 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -382,7 +382,7 @@ std::string GCodePreviewData::get_legend_title() const return ""; } -GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector& tool_colors, const std::vector& cp_values) const +GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const { struct Helper { @@ -465,15 +465,16 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: break; } if (i == 0) { - items.emplace_back((boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0]).str(), color); + items.emplace_back((boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color); break; } if (i == color_print_cnt) { - items.emplace_back((boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i-1]).str(), color); + items.emplace_back((boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i-1].second).str(), color); continue; } - items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color); +// items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color); + items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1].second % cp_values[i].first).str(), color); } break; } diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 6feefd149..9f882788d 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -198,7 +198,7 @@ public: void set_extrusion_paths_colors(const std::vector& colors); std::string get_legend_title() const; - LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector& cp_values) const; + LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const; }; GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 5a5a0735f..ba490e422 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -908,12 +908,12 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector steps; bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { - if ( opt_key == "support_head_front_radius" + if ( opt_key == "supports_enable" + || opt_key == "support_head_front_diameter" || opt_key == "support_head_penetration" - || opt_key == "support_head_back_radius" || opt_key == "support_head_width" - || opt_key == "support_pillar_radius" - || opt_key == "support_base_radius" + || opt_key == "support_pillar_diameter" + || opt_key == "support_base_diameter" || opt_key == "support_base_height" || opt_key == "support_critical_angle" || opt_key == "support_max_bridge_length" @@ -945,18 +945,21 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) if (step == slaposObjectSlice) { invalidated |= this->invalidate_all_steps(); } else if (step == slaposSupportIslands) { - invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports }); + invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); invalidated |= m_print->invalidate_step(slapsRasterize); } else if (step == slaposSupportPoints) { - invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports }); + invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); invalidated |= m_print->invalidate_step(slapsRasterize); } else if (step == slaposSupportTree) { - invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports }); + invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports, slaposIndexSlices }); invalidated |= m_print->invalidate_step(slapsRasterize); } else if (step == slaposBasePool) { - invalidated |= this->invalidate_step(slaposSliceSupports); + invalidated |= this->invalidate_steps({slaposSliceSupports, slaposIndexSlices}); invalidated |= m_print->invalidate_step(slapsRasterize); } else if (step == slaposSliceSupports) { + invalidated |= this->invalidate_step(slaposIndexSlices); + invalidated |= m_print->invalidate_step(slapsRasterize); + } else if(step == slaposIndexSlices) { invalidated |= m_print->invalidate_step(slapsRasterize); } return invalidated; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5cf178c6b..906528e0e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3115,16 +3115,38 @@ GLCanvas3D::LegendTexture::LegendTexture() { } -bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors) +bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors, const GLCanvas3D& canvas) { reset(); // collects items to render auto title = _(preview_data.get_legend_title()); - const auto& config = wxGetApp().preset_bundle->full_config(); - const std::vector& color_print_values = config.option("colorprint_heights")->values; - const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors, color_print_values); + std::vector> cp_legend_values; + if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) + { + const auto& config = wxGetApp().preset_bundle->full_config(); + const std::vector& color_print_values = config.option("colorprint_heights")->values; + const int values_cnt = color_print_values.size(); + if (values_cnt > 0) { + auto print_zs = canvas.get_current_print_zs(true); + auto z = 0; + for (auto i = 0; i < values_cnt; ++i) + { + double prev_z = -1.0; + for (z; z < print_zs.size(); ++z) + if (fabs(color_print_values[i] - print_zs[z]) < EPSILON) { + prev_z = print_zs[z - 1]; + break; + } + if (prev_z < 0) + continue; + + cp_legend_values.push_back(std::pair(prev_z, color_print_values[i])); + } + } + } + const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors, /*color_print_values*/cp_legend_values); unsigned int items_count = (unsigned int)items.size(); if (items_count == 0) @@ -4400,6 +4422,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) // key B/b case 66: case 98: { zoom_to_bed(); break; } + // key I/i + case 73: + case 105: { set_camera_zoom(1.0f); break; } + // key O/o + case 79: + case 111: { set_camera_zoom(-1.0f); break; } #if ENABLE_MODIFIED_CAMERA_TARGET // key Z/z case 90: @@ -4464,18 +4492,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) // Calculate the zoom delta and apply it to the current zoom factor float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); - zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f; - zoom = get_camera_zoom() / (1.0f - zoom); - - // Don't allow to zoom too far outside the scene. - float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); - if (zoom_min > 0.0f) - zoom = std::max(zoom, zoom_min * 0.8f); - - m_camera.zoom = zoom; - viewport_changed(); - - _refresh_if_shown_on_screen(); + set_camera_zoom(zoom); } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -5229,6 +5246,21 @@ void GLCanvas3D::do_mirror() post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } +void GLCanvas3D::set_camera_zoom(float zoom) +{ + zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f; + zoom = get_camera_zoom() / (1.0f - zoom); + + // Don't allow to zoom too far outside the scene. + float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); + if (zoom_min > 0.0f) + zoom = std::max(zoom, zoom_min * 0.8f); + + m_camera.zoom = zoom; + viewport_changed(); + _refresh_if_shown_on_screen(); +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -5908,7 +5940,6 @@ void GLCanvas3D::_render_sla_slices() const { if (obj->is_step_done(slaposIndexSlices)) { - const SLAPrintObject::SliceIndex& index = obj->get_slice_index(); const std::vector& model_slices = obj->get_model_slices(); const std::vector& support_slices = obj->get_support_slices(); const std::vector& instances = obj->instances(); @@ -5928,142 +5959,136 @@ void GLCanvas3D::_render_sla_slices() const double min_z = clip_min_z - shift_z; double max_z = clip_max_z - shift_z; - SLAPrintObject::SliceIndex::const_iterator it_min_z = std::find_if(index.begin(), index.end(), [min_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(min_z - id.first) < EPSILON; }); - SLAPrintObject::SliceIndex::const_iterator it_max_z = std::find_if(index.begin(), index.end(), [max_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(max_z - id.first) < EPSILON; }); - ::glColor3f(1.0f, 0.37f, 0.0f); + Pointf3s bottom_triangles; + Pointf3s top_triangles; - if (it_min_z != index.end()) + if (m_sla_caps[0].matches(min_z)) + bottom_triangles = m_sla_caps[0].triangles; + + if (m_sla_caps[1].matches(max_z)) + top_triangles = m_sla_caps[1].triangles; + + if (bottom_triangles.empty() || top_triangles.empty()) { - // render model bottom slices - if (it_min_z->second.model_slices_idx < model_slices.size()) + const SLAPrintObject::SliceIndex& index = obj->get_slice_index(); + SLAPrintObject::SliceIndex::const_iterator it_min_z = std::find_if(index.begin(), index.end(), [min_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(min_z - id.first) < EPSILON; }); + SLAPrintObject::SliceIndex::const_iterator it_max_z = std::find_if(index.begin(), index.end(), [max_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(max_z - id.first) < EPSILON; }); + + if (bottom_triangles.empty() && (it_min_z != index.end())) { - const ExPolygons& polys = model_slices[it_min_z->second.model_slices_idx]; - for (const ExPolygon& poly : polys) + // calculate model bottom cap + if (it_min_z->second.model_slices_idx < model_slices.size()) { - Polygons triangles; - poly.triangulate(&triangles); - if (!triangles.empty()) + const ExPolygons& polys = model_slices[it_min_z->second.model_slices_idx]; + for (const ExPolygon& poly : polys) { - for (unsigned int i = 0; i < (unsigned int)instances.size(); ++i) + Polygons triangles; + poly.triangulate(&triangles); + for (const Polygon& t : triangles) { - ::glPushMatrix(); - ::glTranslated(instance_transforms[i].offset(0), instance_transforms[i].offset(1), instance_transforms[i].offset(2)); - ::glRotatef(instance_transforms[i].rotation, 0.0, 0.0, 1.0); - - ::glBegin(GL_TRIANGLES); - ::glNormal3f(0.0f, 0.0f, -1.0f); - for (const Polygon& p : triangles) + for (int v = 2; v >= 0; --v) { - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[2]), min_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[1]), min_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[0]), min_z).data()); + bottom_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z)); } - ::glEnd(); - - ::glPopMatrix(); } } } + + // calculate support bottom cap + if (it_min_z->second.support_slices_idx < support_slices.size()) + { + const ExPolygons& polys = support_slices[it_min_z->second.support_slices_idx]; + for (const ExPolygon& poly : polys) + { + Polygons triangles; + poly.triangulate(&triangles); + for (const Polygon& t : triangles) + { + for (int v = 2; v >= 0; --v) + { + bottom_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z)); + } + } + } + } + m_sla_caps[0].z = min_z; + m_sla_caps[0].triangles = bottom_triangles; } - // render support bottom slices - if (it_min_z->second.support_slices_idx < support_slices.size()) + if (top_triangles.empty() && (it_max_z != index.end())) { - const ExPolygons& polys = support_slices[it_min_z->second.support_slices_idx]; - for (const ExPolygon& poly : polys) + // calculate model top cap + if (it_max_z->second.model_slices_idx < model_slices.size()) { - Polygons triangles; - poly.triangulate(&triangles); - if (!triangles.empty()) + const ExPolygons& polys = model_slices[it_max_z->second.model_slices_idx]; + for (const ExPolygon& poly : polys) { - for (unsigned int i = 0; i < (unsigned int)instances.size(); ++i) + Polygons triangles; + poly.triangulate(&triangles); + for (const Polygon& t : triangles) { - ::glPushMatrix(); - ::glTranslated(instance_transforms[i].offset(0), instance_transforms[i].offset(1), instance_transforms[i].offset(2)); - ::glRotatef(instance_transforms[i].rotation, 0.0, 0.0, 1.0); - - ::glBegin(GL_TRIANGLES); - ::glNormal3f(0.0f, 0.0f, -1.0f); - for (const Polygon& p : triangles) + for (int v = 0; v < 3; ++v) { - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[2]), min_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[1]), min_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[0]), min_z).data()); + top_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z)); } - ::glEnd(); - - ::glPopMatrix(); } } } + + // calculate support top cap + if (it_max_z->second.support_slices_idx < support_slices.size()) + { + const ExPolygons& polys = support_slices[it_max_z->second.support_slices_idx]; + for (const ExPolygon& poly : polys) + { + Polygons triangles; + poly.triangulate(&triangles); + for (const Polygon& t : triangles) + { + for (int v = 0; v < 3; ++v) + { + top_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z)); + } + } + } + } + m_sla_caps[1].z = max_z; + m_sla_caps[1].triangles = top_triangles; } } - if (it_max_z != index.end()) + if (!bottom_triangles.empty() || !top_triangles.empty()) { - // render model top slices - if (it_max_z->second.model_slices_idx < model_slices.size()) + ::glColor3f(1.0f, 0.37f, 0.0f); + + for (const InstanceTransform& inst : instance_transforms) { - const ExPolygons& polys = model_slices[it_max_z->second.model_slices_idx]; - for (const ExPolygon& poly : polys) + ::glPushMatrix(); + ::glTranslated(inst.offset(0), inst.offset(1), inst.offset(2)); + ::glRotatef(inst.rotation, 0.0, 0.0, 1.0); + + ::glBegin(GL_TRIANGLES); + + if (!bottom_triangles.empty()) { - Polygons triangles; - poly.triangulate(&triangles); - if (!triangles.empty()) + for (const Vec3d& v : bottom_triangles) { - for (unsigned int i = 0; i < (unsigned int)instances.size(); ++i) - { - ::glPushMatrix(); - ::glTranslated(instance_transforms[i].offset(0), instance_transforms[i].offset(1), instance_transforms[i].offset(2)); - ::glRotatef(instance_transforms[i].rotation, 0.0, 0.0, 1.0); - - ::glBegin(GL_TRIANGLES); - ::glNormal3f(0.0f, 0.0f, 1.0f); - for (const Polygon& p : triangles) - { - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[0]), max_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[1]), max_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[2]), max_z).data()); - } - ::glEnd(); - - ::glPopMatrix(); - } + ::glVertex3dv((GLdouble*)v.data()); } } - } - // render support top slices - if (it_max_z->second.support_slices_idx < support_slices.size()) - { - const ExPolygons& polys = support_slices[it_max_z->second.support_slices_idx]; - for (const ExPolygon& poly : polys) + if (!top_triangles.empty()) { - Polygons triangles; - poly.triangulate(&triangles); - if (!triangles.empty()) + for (const Vec3d& v : top_triangles) { - for (unsigned int i = 0; i < (unsigned int)instances.size(); ++i) - { - ::glPushMatrix(); - ::glTranslated(instance_transforms[i].offset(0), instance_transforms[i].offset(1), instance_transforms[i].offset(2)); - ::glRotatef(instance_transforms[i].rotation, 0.0, 0.0, 1.0); - - ::glBegin(GL_TRIANGLES); - ::glNormal3f(0.0f, 0.0f, 1.0f); - for (const Polygon& p : triangles) - { - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[0]), max_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[1]), max_z).data()); - ::glVertex3dv((GLdouble*)to_3d(unscale(p.points[2]), max_z).data()); - } - ::glEnd(); - - ::glPopMatrix(); - } + ::glVertex3dv((GLdouble*)v.data()); } } + + ::glEnd(); + + ::glPopMatrix(); } } } @@ -7414,7 +7439,7 @@ void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, return; #endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_legend_texture.generate(preview_data, tool_colors); + m_legend_texture.generate(preview_data, tool_colors, *this); } void GLCanvas3D::_generate_warning_texture(const std::string& msg) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0a824ac2c..48e80ae85 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -677,6 +677,16 @@ private: GLGizmoBase* _get_current() const; }; + struct SlaCap + { + double z; + Pointf3s triangles; + + SlaCap() { reset(); } + void reset() { z = DBL_MAX; triangles.clear(); } + bool matches(double z) const { return this->z == z; } + }; + class WarningTexture : public GUI::GLTexture { static const unsigned char Background_Color[3]; @@ -710,7 +720,7 @@ private: public: LegendTexture(); - bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors); + bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors, const GLCanvas3D& canvas); void render(const GLCanvas3D& canvas) const; }; @@ -731,6 +741,7 @@ private: mutable GLToolbar m_toolbar; ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; + mutable SlaCap m_sla_caps[2]; mutable GLVolumeCollection m_volumes; Selection m_selection; @@ -809,7 +820,10 @@ public: void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { if (id < 2) + { m_clipping_planes[id] = plane; + m_sla_caps[id].reset(); + } } void set_use_clipping_planes(bool use) { m_use_clipping_planes = use; } @@ -905,6 +919,8 @@ public: void do_flatten(); void do_mirror(); + void set_camera_zoom(float zoom); + private: bool _is_shown_on_screen() const; void _force_zoom_to_bed(); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index c3587042b..348defc42 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -256,6 +256,15 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const } } +void GLGizmoBase::render_grabbers(float size) const +{ + for (int i = 0; i < (int)m_grabbers.size(); ++i) + { + if (m_grabbers[i].enabled) + m_grabbers[i].render((m_hover_id == i), size); + } +} + void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const { float size = (float)box.max_size(); @@ -821,6 +830,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const Vec3d angles = Vec3d::Zero(); Transform3d offsets_transform = Transform3d::Identity(); + Vec3d grabber_size = Vec3d::Zero(); + if (single_instance) { // calculate bounding box in instance local reference system @@ -839,6 +850,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); + grabber_size = v->get_instance_transformation().get_matrix(true, true, false, true) * box.size(); #else transform = v->world_matrix().cast(); // gets angles from first selected volume @@ -856,6 +868,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const angles = Geometry::extract_euler_angles(transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); + grabber_size = v->get_volume_transformation().get_matrix(true, true, false, true) * box.size(); #else transform = v->world_matrix().cast(); angles = Geometry::extract_euler_angles(transform); @@ -864,7 +877,10 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const #endif // ENABLE_MODELVOLUME_TRANSFORM } else + { box = selection.get_bounding_box(); + grabber_size = box.size(); + } m_box = box; @@ -909,7 +925,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); - float box_max_size = (float)m_box.max_size(); + float grabber_max_size = (float)std::max(grabber_size(0), std::max(grabber_size(1), grabber_size(2))); if (m_hover_id == -1) { @@ -935,7 +951,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const render_grabbers_connection(8, 9); render_grabbers_connection(9, 6); // draw grabbers - render_grabbers(m_box); + render_grabbers(grabber_max_size); } else if ((m_hover_id == 0) || (m_hover_id == 1)) { @@ -943,8 +959,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const ::glColor3fv(m_grabbers[0].color); render_grabbers_connection(0, 1); // draw grabbers - m_grabbers[0].render(true, box_max_size); - m_grabbers[1].render(true, box_max_size); + m_grabbers[0].render(true, grabber_max_size); + m_grabbers[1].render(true, grabber_max_size); } else if ((m_hover_id == 2) || (m_hover_id == 3)) { @@ -952,8 +968,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const ::glColor3fv(m_grabbers[2].color); render_grabbers_connection(2, 3); // draw grabbers - m_grabbers[2].render(true, box_max_size); - m_grabbers[3].render(true, box_max_size); + m_grabbers[2].render(true, grabber_max_size); + m_grabbers[3].render(true, grabber_max_size); } else if ((m_hover_id == 4) || (m_hover_id == 5)) { @@ -961,8 +977,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const ::glColor3fv(m_grabbers[4].color); render_grabbers_connection(4, 5); // draw grabbers - m_grabbers[4].render(true, box_max_size); - m_grabbers[5].render(true, box_max_size); + m_grabbers[4].render(true, grabber_max_size); + m_grabbers[5].render(true, grabber_max_size); } else if (m_hover_id >= 6) { @@ -975,7 +991,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const // draw grabbers for (int i = 6; i < 10; ++i) { - m_grabbers[i].render(true, box_max_size); + m_grabbers[i].render(true, grabber_max_size); } } } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 4ea049239..198a7cb17 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -175,6 +175,7 @@ protected: float picking_color_component(unsigned int id) const; void render_grabbers(const BoundingBoxf3& box) const; + void render_grabbers(float size) const; void render_grabbers_for_picking(const BoundingBoxf3& box) const; void set_tooltip(const std::string& tooltip) const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 5e9a5ec20..73dc658d1 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -426,6 +426,14 @@ void Preview::update_double_slider(const std::vector& layers_z, bool for m_slider->SetTicksValues(ticks_from_config); set_double_slider_thumbs(layers_z, z_low, z_high); + + bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); + if (color_print_enable) { + const auto& config = wxGetApp().preset_bundle->full_config(); + if (config.opt("nozzle_diameter")->values.size() > 1) + color_print_enable = false; + } + m_slider->EnableTickManipulation(color_print_enable); } void Preview::fill_slider_values(std::vector> &values, diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 9cee986b0..256d47253 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_GUI_Utils_hpp_ #define slic3r_GUI_Utils_hpp_ -#include +#include #include #include @@ -10,6 +10,7 @@ #include #include #include +#include class wxCheckBox; class wxTopLevelWindow; @@ -25,40 +26,80 @@ wxTopLevelWindow* find_toplevel_parent(wxWindow *window); class EventGuard { + // This is a RAII-style smart-ptr-like guard that will bind any event to any event handler + // and unbind it as soon as it goes out of scope or unbind() is called. + // This can be used to solve the annoying problem of wx events being delivered to freed objects. + +private: + // This is a way to type-erase both the event type as well as the handler: + + struct EventStorageBase { + virtual ~EventStorageBase() {} + }; + + template + struct EventStorageFun : EventStorageBase { + wxEvtHandler *emitter; + EvTag tag; + Fun fun; + + EventStorageFun(wxEvtHandler *emitter, const EvTag &tag, Fun fun) + : emitter(emitter) + , tag(tag) + , fun(std::move(fun)) + { + emitter->Bind(this->tag, this->fun); + } + + virtual ~EventStorageFun() { emitter->Unbind(tag, fun); } + }; + + template + struct EventStorageMethod : EventStorageBase { + typedef void(Class::* MethodPtr)(EvArg &); + + wxEvtHandler *emitter; + EvTag tag; + MethodPtr method; + EvHandler *handler; + + EventStorageMethod(wxEvtHandler *emitter, const EvTag &tag, MethodPtr method, EvHandler *handler) + : emitter(emitter) + , tag(tag) + , method(method) + , handler(handler) + { + emitter->Bind(tag, method, handler); + } + + virtual ~EventStorageMethod() { emitter->Unbind(tag, method, handler); } + }; + + std::unique_ptr event_storage; public: EventGuard() {} EventGuard(const EventGuard&) = delete; - EventGuard(EventGuard &&other) : unbinder(std::move(other.unbinder)) {} + EventGuard(EventGuard &&other) : event_storage(std::move(other.event_storage)) {} - ~EventGuard() { - if (unbinder) { - unbinder(false); - } - } + template + EventGuard(wxEvtHandler *emitter, const EvTag &tag, Fun fun) + :event_storage(new EventStorageFun(emitter, tag, std::move(fun))) + {} - template void bind(wxEvtHandler *emitter, const EvTag &type, Fun fun) - { - // This is a way to type-erase both the event type as well as the handler: - - unbinder = std::move([=](bool bind) { - if (bind) { - emitter->Bind(type, fun); - } else { - emitter->Unbind(type, fun); - } - }); - - unbinder(true); - } + template + EventGuard(wxEvtHandler *emitter, const EvTag &tag, void(Class::* method)(EvArg &), EvHandler *handler) + :event_storage(new EventStorageMethod(emitter, tag, method, handler)) + {} EventGuard& operator=(const EventGuard&) = delete; EventGuard& operator=(EventGuard &&other) { - unbinder.swap(other.unbinder); + event_storage = std::move(other.event_storage); return *this; } -private: - std::function unbinder; + + void unbind() { event_storage.reset(nullptr); } + explicit operator bool() const noexcept { return !!event_storage; } }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bb4441bf2..0ea0d1b16 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -914,6 +914,10 @@ struct Plater::priv // GUI elements wxNotebook *notebook; + EventGuard guard_on_notebook_changed; + // Note: ^ The on_notebook_changed is guarded here because the wxNotebook d-tor tends to generate + // wxEVT_NOTEBOOK_PAGE_CHANGED events on some platforms, which causes them to be received by a freed Plater. + // EventGuard unbinds the handler in its d-tor. Sidebar *sidebar; #if !ENABLE_IMGUI wxPanel *panel3d; @@ -1042,6 +1046,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology" })) , notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)) + , guard_on_notebook_changed(notebook, wxEVT_NOTEBOOK_PAGE_CHANGED, &priv::on_notebook_changed, this) , sidebar(new Sidebar(q)) #if ENABLE_IMGUI , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(notebook)) @@ -1124,9 +1129,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Events: - // Notebook page change event - notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &priv::on_notebook_changed, this); - // Preset change event sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this); @@ -1606,6 +1608,10 @@ void Plater::priv::reset() sidebar->obj_list()->delete_all_objects_from_list(); object_list_changed(); update(); + + + auto& config = wxGetApp().preset_bundle->project_config; + config.option("colorprint_heights")->values.clear(); } void Plater::priv::mirror(Axis axis) @@ -1740,6 +1746,9 @@ void Plater::priv::sla_optimize_rotation() { if(rotoptimizing.load()) // wasn't canceled for(ModelInstance * oi : o->instances) oi->set_rotation({r[X], r[Y], r[Z]}); + // Correct the z offset of the object which was corrupted be the rotation + o->ensure_on_bed(); + stfn(0, L("Orientation found.")); statusbar()->set_range(prev_range); statusbar()->set_cancel_callback(); @@ -1935,6 +1944,8 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) { + wxCHECK_RET(canvas3D != nullptr, "on_notebook_changed on freed Plater"); + const auto current_id = notebook->GetCurrentPage()->GetId(); #if ENABLE_IMGUI if (current_id == canvas3Dwidget->GetId()) { diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 398825a7d..4b9742220 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1446,7 +1446,7 @@ void PrusaDoubleSlider::get_size(int *w, int *h) double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) { - if (m_values.empty()) + if (m_values.empty() || m_lower_value<0) return 0.0; if (m_values.size() <= m_higher_value) { correct_higher_value(); @@ -1569,7 +1569,8 @@ void PrusaDoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, c dc.DrawLine(pt_beg, pt_end); //draw action icon - draw_action_icon(dc, pt_beg, pt_end); + if (m_is_enabled_tick_manipulation) + draw_action_icon(dc, pt_beg, pt_end); } } @@ -1677,7 +1678,7 @@ void PrusaDoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wx void PrusaDoubleSlider::draw_ticks(wxDC& dc) { - dc.SetPen(DARK_GREY_PEN); + dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); int height, width; get_size(&width, &height); const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; @@ -1794,7 +1795,7 @@ void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event) this->CaptureMouse(); wxClientDC dc(this); wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action)) { + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) { action_tick(taOnIcon); return; } @@ -1812,7 +1813,7 @@ void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event) else detect_selected_slider(pos); - if (!m_selection) { + if (!m_selection && m_is_enabled_tick_manipulation) { const auto tick = is_point_near_tick(pos); if (tick >= 0) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 27d233e6f..4829a0aef 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -683,6 +683,12 @@ public: void ChangeOneLayerLock(); std::vector GetTicksValues() const; void SetTicksValues(const std::vector& heights); + void EnableTickManipulation(bool enable = true) { + m_is_enabled_tick_manipulation = enable; + } + void DisableTickManipulation() { + EnableTickManipulation(false); + } void OnPaint(wxPaintEvent& ) { render();} void OnLeftDown(wxMouseEvent& event); @@ -753,6 +759,7 @@ private: bool m_is_focused = false; bool m_is_action_icon_focesed = false; bool m_is_one_layer_icon_focesed = false; + bool m_is_enabled_tick_manipulation = true; wxRect m_rect_lower_thumb; wxRect m_rect_higher_thumb;