diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs index 850d69b083..ed03bfe64e 100644 --- a/resources/shaders/gouraud.fs +++ b/resources/shaders/gouraud.fs @@ -4,7 +4,9 @@ const vec3 ZERO = vec3(0.0, 0.0, 0.0); const vec3 GREEN = vec3(0.0, 0.7, 0.0); const vec3 YELLOW = vec3(0.5, 0.7, 0.0); const vec3 RED = vec3(0.7, 0.0, 0.0); +const vec3 WHITE = vec3(1.0, 1.0, 1.0); const float EPSILON = 0.0001; +const float BANDS_WIDTH = 10.0; struct SlopeDetection { @@ -15,6 +17,7 @@ struct SlopeDetection uniform vec4 uniform_color; uniform SlopeDetection slope; +uniform bool sinking; #ifdef ENABLE_ENVIRONMENT_MAP uniform sampler2D environment_tex; @@ -23,27 +26,38 @@ uniform SlopeDetection slope; varying vec3 clipping_planes_dots; -// x = tainted, y = specular; +// x = diffuse, y = specular; varying vec2 intensity; varying vec3 delta_box_min; varying vec3 delta_box_max; +varying vec4 model_pos; +varying float world_pos_z; varying float world_normal_z; varying vec3 eye_normal; +vec3 sinking_color(vec3 color) +{ + return (mod(model_pos.x + model_pos.y + model_pos.z, BANDS_WIDTH) < (0.5 * BANDS_WIDTH)) ? mix(color, ZERO, 0.6666) : color; +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) discard; vec3 color = uniform_color.rgb; float alpha = uniform_color.a; - if (slope.actived && world_normal_z < slope.normal_z - EPSILON) { + if (slope.actived && world_normal_z < slope.normal_z - EPSILON) + { color = vec3(0.7, 0.7, 1.0); alpha = 1.0; } // if the fragment is outside the print volume -> use darker color color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : color; + // if the object is sinking, shade it with inclined bands or white around world z = 0 + if (sinking) + color = (abs(world_pos_z) < 0.05) ? WHITE : sinking_color(color); #ifdef ENABLE_ENVIRONMENT_MAP if (use_environment_tex) gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha); diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs index ed7e3f56ba..f2706b386c 100644 --- a/resources/shaders/gouraud.vs +++ b/resources/shaders/gouraud.vs @@ -41,7 +41,7 @@ uniform vec2 z_range; // Clipping plane - general orientation. Used by the SLA gizmo. uniform vec4 clipping_plane; -// x = tainted, y = specular; +// x = diffuse, y = specular; varying vec2 intensity; varying vec3 delta_box_min; @@ -49,6 +49,8 @@ varying vec3 delta_box_max; varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying float world_pos_z; varying float world_normal_z; varying vec3 eye_normal; @@ -69,12 +71,16 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + model_pos = gl_Vertex; + // Point in homogenous coordinates. + vec4 world_pos = print_box.volume_world_matrix * gl_Vertex; + world_pos_z = world_pos.z; + // compute deltas for out of print volume detection (world coordinates) if (print_box.actived) { - vec3 v = (print_box.volume_world_matrix * gl_Vertex).xyz; - delta_box_min = v - print_box.min; - delta_box_max = v - print_box.max; + delta_box_min = world_pos.xyz - print_box.min; + delta_box_max = world_pos.xyz - print_box.max; } else { @@ -86,8 +92,6 @@ void main() world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0; gl_Position = ftransform(); - // Point in homogenous coordinates. - vec4 world_pos = print_box.volume_world_matrix * gl_Vertex; // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); } diff --git a/resources/shaders/variable_layer_height.fs b/resources/shaders/variable_layer_height.fs index f87e6627e4..693c1c6a0b 100644 --- a/resources/shaders/variable_layer_height.fs +++ b/resources/shaders/variable_layer_height.fs @@ -24,7 +24,7 @@ void main() float z_texture_col = object_z_row - z_texture_row; float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; // Calculate level of detail from the object Z coordinate. - // This makes the slowly sloping surfaces to be show with high detail (with stripes), + // This makes the slowly sloping surfaces to be shown with high detail (with stripes), // and the vertical surfaces to be shown with low detail (no stripes) float z_in_cells = object_z_row * 190.; // Gradient of Z projected on the screen. @@ -32,9 +32,10 @@ void main() float dy_vtc = dFdy(z_in_cells); float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.); // Sample the Z texture. Texture coordinates are normalized to <0, 1>. - vec4 color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), - texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); - + vec4 color = vec4(0.25, 0.25, 0.25, 1.0); + if (z_texture_row >= 0.0) + color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), + texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); // Mix the final color. - gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); + gl_FragColor = vec4(vec3(intensity.y), 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index bfad61a90d..a02eb738bf 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -942,30 +942,39 @@ void ModelObject::center_around_origin(bool include_modifiers) { // calculate the displacements needed to // center this object around the origin - BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box(); + const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box(); // Shift is the vector from the center of the bounding box to the origin - Vec3d shift = -bb.center(); + const Vec3d shift = -bb.center(); this->translate(shift); this->origin_translation += shift; } +#if ENABLE_ALLOW_NEGATIVE_Z +void ModelObject::ensure_on_bed(bool allow_negative_z) +{ + const double min_z = get_min_z(); + if (!allow_negative_z || min_z > 0.0) + translate_instances({ 0.0, 0.0, -min_z }); +} +#else void ModelObject::ensure_on_bed() { - translate_instances(Vec3d(0.0, 0.0, -get_min_z())); + translate_instances({ 0.0, 0.0, -get_min_z() }); } +#endif // ENABLE_ALLOW_NEGATIVE_Z void ModelObject::translate_instances(const Vec3d& vector) { - for (size_t i = 0; i < instances.size(); ++i) - { + for (size_t i = 0; i < instances.size(); ++i) { translate_instance(i, vector); } } void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector) { + assert(instance_idx < instances.size()); ModelInstance* i = instances[instance_idx]; i->set_offset(i->get_offset() + vector); invalidate_bounding_box(); @@ -973,8 +982,7 @@ void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector) void ModelObject::translate(double x, double y, double z) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->translate(x, y, z); } @@ -984,8 +992,7 @@ void ModelObject::translate(double x, double y, double z) void ModelObject::scale(const Vec3d &versor) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->scale(versor); } this->invalidate_bounding_box(); @@ -993,41 +1000,34 @@ void ModelObject::scale(const Vec3d &versor) void ModelObject::rotate(double angle, Axis axis) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->rotate(angle, axis); } - center_around_origin(); this->invalidate_bounding_box(); } void ModelObject::rotate(double angle, const Vec3d& axis) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->rotate(angle, axis); } - center_around_origin(); this->invalidate_bounding_box(); } void ModelObject::mirror(Axis axis) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->mirror(axis); } - this->invalidate_bounding_box(); } // This method could only be called before the meshes of this ModelVolumes are not shared! void ModelObject::scale_mesh_after_creation(const Vec3d &versor) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->scale_geometry_after_creation(versor); v->set_offset(versor.cwiseProduct(v->get_offset())); } @@ -1418,11 +1418,9 @@ double ModelObject::get_min_z() const { if (instances.empty()) return 0.0; - else - { + else { double min_z = DBL_MAX; - for (size_t i = 0; i < instances.size(); ++i) - { + for (size_t i = 0; i < instances.size(); ++i) { min_z = std::min(min_z, get_instance_min_z(i)); } return min_z; @@ -1433,15 +1431,14 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const { double min_z = DBL_MAX; - ModelInstance* inst = instances[instance_idx]; + const ModelInstance* inst = instances[instance_idx]; const Transform3d& mi = inst->get_matrix(true); - for (const ModelVolume* v : volumes) - { + for (const ModelVolume* v : volumes) { if (!v->is_model_part()) continue; - Transform3d mv = mi * v->get_matrix(); + const Transform3d mv = mi * v->get_matrix(); const TriangleMesh& hull = v->get_convex_hull(); for (const stl_facet &facet : hull.stl.facet_start) for (int i = 0; i < 3; ++ i) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a65332272c..ffb6dc7182 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -306,7 +306,11 @@ public: void center_around_origin(bool include_modifiers = true); +#if ENABLE_ALLOW_NEGATIVE_Z + void ensure_on_bed(bool allow_negative_z = false); +#else void ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z void translate_instances(const Vec3d& vector); void translate_instance(size_t instance_idx, const Vec3d& vector); void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2987b7a237..46069849ba 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1628,9 +1628,15 @@ PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegion void PrintObject::update_slicing_parameters() { +#if ENABLE_ALLOW_NEGATIVE_Z + if (!m_slicing_params.valid) + m_slicing_params = SlicingParameters::create_from_config( + this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders()); +#else if (! m_slicing_params.valid) m_slicing_params = SlicingParameters::create_from_config( this->print()->config(), m_config, unscale(this->height()), this->object_extruders()); +#endif // ENABLE_ALLOW_NEGATIVE_Z } SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z) @@ -1692,6 +1698,15 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c updated = true; } +#if ENABLE_ALLOW_NEGATIVE_Z + // Verify the layer_height_profile. + if (!layer_height_profile.empty() && + // Must not be of even length. + ((layer_height_profile.size() & 1) != 0 || + // Last entry must be at the top of the object. + std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max) > 1e-3)) + layer_height_profile.clear(); +#else // Verify the layer_height_profile. if (! layer_height_profile.empty() && // Must not be of even length. @@ -1699,6 +1714,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // Last entry must be at the top of the object. std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3)) layer_height_profile.clear(); +#endif // ENABLE_ALLOW_NEGATIVE_Z if (layer_height_profile.empty()) { //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 99684e93e0..480ca0ee5b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -65,11 +65,13 @@ #define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0) // Enable visualization of seams in preview #define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0) - // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window #define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) +// Enable to push object instances under the bed +#define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0) +#define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ba62576f23..00bad52ff7 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -23,6 +23,9 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/AppConfig.hpp" +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA +#include "libslic3r/PresetBundle.hpp" +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA #include #include @@ -345,9 +348,16 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size) void GLVolume::set_render_color() { - if (force_native_color || force_neutral_color) - { +#if ENABLE_ALLOW_NEGATIVE_Z + bool outside = is_outside || is_below_printbed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z + + if (force_native_color || force_neutral_color) { +#if ENABLE_ALLOW_NEGATIVE_Z + if (outside && shader_outside_printer_detection_enabled) +#else if (is_outside && shader_outside_printer_detection_enabled) +#endif // ENABLE_ALLOW_NEGATIVE_Z set_render_color(OUTSIDE_COLOR, 4); else { if (force_native_color) @@ -362,17 +372,24 @@ void GLVolume::set_render_color() else if (hover == HS_Deselect) set_render_color(HOVER_DESELECT_COLOR, 4); else if (selected) +#if ENABLE_ALLOW_NEGATIVE_Z + set_render_color(outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); +#else set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); +#endif // ENABLE_ALLOW_NEGATIVE_Z else if (disabled) set_render_color(DISABLED_COLOR, 4); +#if ENABLE_ALLOW_NEGATIVE_Z + else if (outside && shader_outside_printer_detection_enabled) +#else else if (is_outside && shader_outside_printer_detection_enabled) +#endif // ENABLE_ALLOW_NEGATIVE_Z set_render_color(OUTSIDE_COLOR, 4); else set_render_color(color, 4); } - if (!printable) - { + if (!printable) { render_color[0] /= 4; render_color[1] /= 4; render_color[2] /= 4; @@ -504,6 +521,25 @@ void GLVolume::render() const bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); } bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); } +#if ENABLE_ALLOW_NEGATIVE_Z +bool GLVolume::is_sinking() const +{ +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (is_modifier || GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) +#else + if (is_modifier) +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + return false; + const BoundingBoxf3& box = transformed_convex_hull_bounding_box(); + return box.min(2) < -EPSILON && box.max(2) >= -EPSILON; +} + +bool GLVolume::is_below_printbed() const +{ + return transformed_convex_hull_bounding_box().max(2) < 0.0; +} +#endif // ENABLE_ALLOW_NEGATIVE_Z + std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -778,6 +814,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.volume_world_normal_matrix", static_cast(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast())); +#if ENABLE_ALLOW_NEGATIVE_Z + shader->set_uniform("sinking", volume.first->is_sinking()); +#endif // ENABLE_ALLOW_NEGATIVE_Z volume.first->render(); } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 25c5443cda..2d46a4524a 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -453,6 +453,11 @@ public: bool is_sla_support() const; bool is_sla_pad() const; +#if ENABLE_ALLOW_NEGATIVE_Z + bool is_sinking() const; + bool is_below_printbed() const; +#endif // ENABLE_ALLOW_NEGATIVE_Z + // Return an estimate of the memory consumed by this class. size_t cpu_memory_used() const { //FIXME what to do wih m_convex_hull? diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 39214221f8..8873af245d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -134,27 +134,9 @@ void Size::set_scale_factor(int scale_factor) m_scale_factor = scale_factor; } -GLCanvas3D::LayersEditing::LayersEditing() - : m_enabled(false) - , m_z_texture_id(0) - , m_model_object(nullptr) - , m_object_max_z(0.f) - , m_slicing_parameters(nullptr) - , m_layer_height_profile_modified(false) - , m_adaptive_quality(0.5f) - , state(Unknown) - , band_width(2.0f) - , strength(0.005f) - , last_object_id(-1) - , last_z(0.0f) - , last_action(LAYER_HEIGHT_EDIT_ACTION_INCREASE) -{ -} - GLCanvas3D::LayersEditing::~LayersEditing() { - if (m_z_texture_id != 0) - { + if (m_z_texture_id != 0) { glsafe(::glDeleteTextures(1, &m_z_texture_id)); m_z_texture_id = 0; } @@ -186,11 +168,18 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) { const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr; +#if ENABLE_ALLOW_NEGATIVE_Z + // Maximum height of an object changes when the object gets rotated or scaled. + // Changing maximum height of an object will invalidate the layer heigth editing profile. + // m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently. + const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast(model_object_new->bounding_box().max.z()); +#else // Maximum height of an object changes when the object gets rotated or scaled. // Changing maximum height of an object will invalidate the layer heigth editing profile. // m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently. float new_max_z = (model_object_new == nullptr) ? 0.f : model_object_new->raw_bounding_box().size().z(); - if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z || +#endif // ENABLE_ALLOW_NEGATIVE_Z + if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z || (model_object_new != nullptr && m_model_object->id() != model_object_new->id())) { m_layer_height_profile.clear(); m_layer_height_profile_modified = false; @@ -218,7 +207,7 @@ void GLCanvas3D::LayersEditing::set_enabled(bool enabled) m_enabled = is_allowed() && enabled; } -float GLCanvas3D::LayersEditing::s_overelay_window_width; +float GLCanvas3D::LayersEditing::s_overlay_window_width; void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const { @@ -302,7 +291,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (imgui.button(_L("Reset"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); - GLCanvas3D::LayersEditing::s_overelay_window_width = ImGui::GetWindowSize().x /*+ (float)m_layers_texture.width/4*/; + GLCanvas3D::LayersEditing::s_overlay_window_width = ImGui::GetWindowSize().x /*+ (float)m_layers_texture.width/4*/; imgui.end(); const Rect& bar_rect = get_bar_rect_viewport(canvas); @@ -319,7 +308,7 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) float t = rect.get_top(); float b = rect.get_bottom(); - return ((rect.get_left() <= x) && (x <= rect.get_right()) && (t <= y) && (y <= b)) ? + return (rect.get_left() <= x && x <= rect.get_right() && t <= y && y <= b) ? // Inside the bar. (b - y - 1.0f) / (b - t - 1.0f) : // Outside the bar. @@ -329,7 +318,7 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) { const Rect& rect = get_bar_rect_screen(canvas); - return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); + return rect.get_left() <= x && x <= rect.get_right() && rect.get_top() <= y && y <= rect.get_bottom(); } Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) @@ -338,7 +327,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); - return Rect(w - thickness_bar_width(canvas), 0.0f, w, h); + return { w - thickness_bar_width(canvas), 0.0f, w, h }; } Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) @@ -347,7 +336,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) float half_w = 0.5f * (float)cnv_size.get_width(); float half_h = 0.5f * (float)cnv_size.get_height(); float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); + return { (half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom }; } bool GLCanvas3D::LayersEditing::is_initialized() const @@ -365,11 +354,12 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con float h = 0.0f; for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2) { - float zi = m_layer_height_profile[i]; - float zi_1 = m_layer_height_profile[i - 2]; + const float zi = static_cast(m_layer_height_profile[i]); + const float zi_1 = static_cast(m_layer_height_profile[i - 2]); if (zi_1 <= z && z <= zi) { float dz = zi - zi_1; - h = (dz != 0.0f) ? lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz) : m_layer_height_profile[i + 1]; + h = (dz != 0.0f) ? static_cast(lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz)) : + static_cast(m_layer_height_profile[i + 1]); break; } } @@ -398,10 +388,10 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); // Render the color bar - float l = bar_rect.get_left(); - float r = bar_rect.get_right(); - float t = bar_rect.get_top(); - float b = bar_rect.get_bottom(); + const float l = bar_rect.get_left(); + const float r = bar_rect.get_right(); + const float t = bar_rect.get_top(); + const float b = bar_rect.get_bottom(); ::glBegin(GL_QUADS); ::glNormal3f(0.0f, 0.0f, 1.0f); @@ -564,7 +554,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) { if (last_object_id >= 0) { if (m_layer_height_profile_modified) { - wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit"))); + wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit")); const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); wxGetApp().obj_list()->update_info_items(last_object_id); @@ -577,7 +567,7 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters() { if (m_slicing_parameters == nullptr) { m_slicing_parameters = new SlicingParameters(); - *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); } } @@ -1961,7 +1951,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_reload_delayed = !m_canvas->IsShown() && !refresh_immediately && !force_full_scene_refresh; - PrinterTechnology printer_technology = m_process->current_printer_technology(); + PrinterTechnology printer_technology = current_printer_technology(); int volume_idx_wipe_tower_old = -1; // Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed). @@ -3578,14 +3568,38 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) wipe_tower_origin = v->get_volume_offset(); } +#if ENABLE_ALLOW_NEGATIVE_Z + // Fixes flying instances +#else // Fixes sinking/flying instances +#endif // ENABLE_ALLOW_NEGATIVE_Z for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; +#if ENABLE_ALLOW_NEGATIVE_Z + double shift_z = m->get_instance_min_z(i.second); +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (current_printer_technology() == ptSLA || shift_z > 0.0) { +#else + if (shift_z > 0.0) { +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + Vec3d shift(0.0, 0.0, -shift_z); +#else Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); +#endif // ENABLE_ALLOW_NEGATIVE_Z m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + } +#endif // ENABLE_ALLOW_NEGATIVE_Z } +#if ENABLE_ALLOW_NEGATIVE_Z + // if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running + // similar to void Plater::priv::selection_changed() + if (!wxGetApp().plater()->can_layers_editing() && is_layers_editing_enabled()) + post_event(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); +#endif // ENABLE_ALLOW_NEGATIVE_Z + if (object_moved) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); @@ -3603,18 +3617,30 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(_(snapshot_type)); +#if ENABLE_ALLOW_NEGATIVE_Z + // stores current min_z of instances + std::map, double> min_zs; + if (!snapshot_type.empty()) { + for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { + const ModelObject* obj = m_model->objects[i]; + for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + min_zs[{ i, j }] = obj->instance_bounding_box(j).min(2); + } + } + } +#endif // ENABLE_ALLOW_NEGATIVE_Z + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); - for (const GLVolume* v : m_volumes.volumes) - { + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); if (object_idx == 1000) { // the wipe tower Vec3d offset = v->get_volume_offset(); post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset(0), offset(1), v->get_volume_rotation()(2)))); } - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) continue; int instance_idx = v->instance_idx(); @@ -3624,15 +3650,12 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) // Rotate instances/volumes. ModelObject* model_object = m_model->objects[object_idx]; - if (model_object != nullptr) - { - if (selection_mode == Selection::Instance) - { + if (model_object != nullptr) { + if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); } - else if (selection_mode == Selection::Volume) - { + else if (selection_mode == Selection::Volume) { model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); } @@ -3641,12 +3664,21 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) } // Fixes sinking/flying instances - for (const std::pair& i : done) - { + for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; +#if ENABLE_ALLOW_NEGATIVE_Z + double shift_z = m->get_instance_min_z(i.second); + // leave sinking instances as sinking + if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= 0.0 || shift_z > 0.0) { + Vec3d shift(0.0, 0.0, -shift_z); +#else Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); - m_selection.translate(i.first, i.second, shift); - m->translate_instance(i.second, shift); +#endif // ENABLE_ALLOW_NEGATIVE_Z + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + } +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (!done.empty()) @@ -3663,14 +3695,26 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(_(snapshot_type)); +#if ENABLE_ALLOW_NEGATIVE_Z + // stores current min_z of instances + std::map, double> min_zs; + if (!snapshot_type.empty()) { + for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { + const ModelObject* obj = m_model->objects[i]; + for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + min_zs[{ i, j }] = obj->instance_bounding_box(j).min(2); + } + } + } +#endif // ENABLE_ALLOW_NEGATIVE_Z + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); - for (const GLVolume* v : m_volumes.volumes) - { + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) continue; int instance_idx = v->instance_idx(); @@ -3680,15 +3724,12 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) // Rotate instances/volumes ModelObject* model_object = m_model->objects[object_idx]; - if (model_object != nullptr) - { - if (selection_mode == Selection::Instance) - { + if (model_object != nullptr) { + if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); } - else if (selection_mode == Selection::Volume) - { + else if (selection_mode == Selection::Volume) { model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); @@ -3698,16 +3739,25 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) } // Fixes sinking/flying instances - for (const std::pair& i : done) - { + for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; +#if ENABLE_ALLOW_NEGATIVE_Z + double shift_z = m->get_instance_min_z(i.second); + // leave sinking instances as sinking + if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= 0.0 || shift_z > 0.0) { + Vec3d shift(0.0, 0.0, -shift_z); +#else Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); +#endif // ENABLE_ALLOW_NEGATIVE_Z m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + } +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (!done.empty()) - post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); m_dirty = true; } @@ -3875,7 +3925,7 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() { std::string new_tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + - "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; m_main_toolbar.set_tooltip(get_main_toolbar_item_id("settings"), new_tooltip); @@ -4029,7 +4079,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) ArrangeSettings &settings_out = get_arrange_settings(); auto &appcfg = wxGetApp().app_config; - PrinterTechnology ptech = m_process->current_printer_technology(); + PrinterTechnology ptech = current_printer_technology(); bool settings_changed = false; float dist_min = 0.f; @@ -4596,7 +4646,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "settings"; item.icon_filename = "settings.svg"; item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + - "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; item.sprite_id = 10; item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; @@ -4638,7 +4688,7 @@ bool GLCanvas3D::_init_main_toolbar() item.sprite_id = 12; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { - bool res = m_process->current_printer_technology() == ptFFF; + bool res = current_printer_technology() == ptFFF; // turns off if changing printer technology if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting")) force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0e1bb7229c..10457d8771 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -154,53 +154,50 @@ class GLCanvas3D static const float THICKNESS_BAR_WIDTH; private: - bool m_enabled; - unsigned int m_z_texture_id; + bool m_enabled{ false }; + unsigned int m_z_texture_id{ 0 }; // Not owned by LayersEditing. - const DynamicPrintConfig *m_config; + const DynamicPrintConfig *m_config{ nullptr }; // ModelObject for the currently selected object (Model::objects[last_object_id]). - const ModelObject *m_model_object; + const ModelObject *m_model_object{ nullptr }; // Maximum z of the currently selected object (Model::objects[last_object_id]). - float m_object_max_z; + float m_object_max_z{ 0.0f }; // Owned by LayersEditing. - SlicingParameters *m_slicing_parameters; + SlicingParameters *m_slicing_parameters{ nullptr }; std::vector m_layer_height_profile; - bool m_layer_height_profile_modified; + bool m_layer_height_profile_modified{ false }; - mutable float m_adaptive_quality; + mutable float m_adaptive_quality{ 0.5f }; mutable HeightProfileSmoothingParams m_smooth_params; - static float s_overelay_window_width; + static float s_overlay_window_width; - class LayersTexture + struct LayersTexture { - public: - LayersTexture() : width(0), height(0), levels(0), cells(0), valid(false) {} - // Texture data std::vector data; // Width of the texture, top level. - size_t width; + size_t width{ 0 }; // Height of the texture, top level. - size_t height; + size_t height{ 0 }; // For how many levels of detail is the data allocated? - size_t levels; + size_t levels{ 0 }; // Number of texture cells allocated for the height texture. - size_t cells; + size_t cells{ 0 }; // Does it need to be refreshed? - bool valid; + bool valid{ false }; }; LayersTexture m_layers_texture; public: - EState state; - float band_width; - float strength; - int last_object_id; - float last_z; - LayerHeightEditActionType last_action; + EState state{ Unknown }; + float band_width{ 2.0f }; + float strength{ 0.005f }; + int last_object_id{ -1 }; + float last_z{ 0.0f }; + LayerHeightEditActionType last_action{ LAYER_HEIGHT_EDIT_ACTION_INCREASE }; - LayersEditing(); + LayersEditing() = default; ~LayersEditing(); void init(); @@ -226,7 +223,7 @@ class GLCanvas3D static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); static Rect get_bar_rect_screen(const GLCanvas3D& canvas); static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); - static float get_overlay_window_width() { return LayersEditing::s_overelay_window_width; } + static float get_overlay_window_width() { return LayersEditing::s_overlay_window_width; } float object_max_z() const { return m_object_max_z; } diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 725a396c34..d079a6ebdb 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -142,7 +142,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(editor); - auto temp = new wxStaticText(m_parent, wxID_ANY, _(L("mm"))); + auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm")); temp->SetBackgroundStyle(wxBG_STYLE_PAINT); temp->SetFont(wxGetApp().normal_font()); sizer->Add(temp, 0, wxLEFT, wxGetApp().em_unit()); @@ -154,15 +154,14 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus void ObjectLayers::create_layers_list() { - for (const auto &layer : m_object->layer_config_ranges) - { + for (const auto &layer : m_object->layer_config_ranges) { const t_layer_height_range& range = layer.first; auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range); - del_btn->SetToolTip(_(L("Remove layer range"))); + del_btn->SetToolTip(_L("Remove layer range")); auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range); wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range); - add_btn->SetToolTip(tooltip.IsEmpty() ? _(L("Add layer range")) : tooltip); + add_btn->SetToolTip(tooltip.IsEmpty() ? _L("Add layer range") : tooltip); add_btn->Enable(tooltip.IsEmpty()); auto sizer = create_layer(range, del_btn, add_btn); @@ -242,11 +241,9 @@ void ObjectLayers::msw_rescale() // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; i++) - { + for (int i = 0; i < cells_cnt; ++i) { const wxSizerItem* item = m_grid_sizer->GetItem(i); - if (item->IsWindow()) - { + if (item->IsWindow()) { LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); if (editor != nullptr) editor->msw_rescale(); @@ -283,8 +280,7 @@ void ObjectLayers::sys_color_changed() // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; i++) - { + for (int i = 0; i < cells_cnt; ++i) { const wxSizerItem* item = m_grid_sizer->GetItem(i); if (item->IsSizer()) {// case when we have editor with buttons const std::vector btns = {2, 3}; // del_btn, add_btn @@ -405,11 +401,9 @@ coordf_t LayerRangeEditor::get_value() str.Replace(",", ".", false); if (str == ".") layer_height = 0.0; - else - { - if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) - { - show_error(m_parent, _(L("Invalid numeric input."))); + else { + if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) { + show_error(m_parent, _L("Invalid numeric input.")); SetValue(double_to_string(layer_height)); } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4bf3bdfd19..28053971f3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2119,18 +2119,15 @@ void ObjectList::part_selection_changed() const auto item = GetSelection(); - if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) - { - og_name = _(L("Group manipulation")); + if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { + og_name = _L("Group manipulation"); const Selection& selection = scene_selection(); // don't show manipulation panel for case of all Object's parts selection update_and_show_manipulations = !selection.is_single_full_instance(); } - else - { - if (item) - { + else { + if (item) { const ItemType type = m_objects_model->GetItemType(item); const wxDataViewItem parent = m_objects_model->GetParent(item); const ItemType parent_type = m_objects_model->GetItemType(parent); @@ -2138,7 +2135,7 @@ void ObjectList::part_selection_changed() if (parent == wxDataViewItem(nullptr) || type == itInfo) { - og_name = _(L("Object manipulation")); + og_name = _L("Object manipulation"); m_config = &(*m_objects)[obj_idx]->config; update_and_show_manipulations = true; @@ -2158,35 +2155,35 @@ void ObjectList::part_selection_changed() else { if (type & itSettings) { if (parent_type & itObject) { - og_name = _(L("Object Settings to modify")); + og_name = _L("Object Settings to modify"); m_config = &(*m_objects)[obj_idx]->config; } else if (parent_type & itVolume) { - og_name = _(L("Part Settings to modify")); + og_name = _L("Part Settings to modify"); volume_id = m_objects_model->GetVolumeIdByItem(parent); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; } else if (parent_type & itLayer) { - og_name = _(L("Layer range Settings to modify")); + og_name = _L("Layer range Settings to modify"); m_config = &get_item_config(parent); } update_and_show_settings = true; } else if (type & itVolume) { - og_name = _(L("Part manipulation")); + og_name = _L("Part manipulation"); volume_id = m_objects_model->GetVolumeIdByItem(item); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_manipulations = true; } else if (type & itInstance) { - og_name = _(L("Instance manipulation")); + og_name = _L("Instance manipulation"); update_and_show_manipulations = true; // fill m_config by object's values m_config = &(*m_objects)[obj_idx]->config; } else if (type & (itLayerRoot|itLayer)) { - og_name = type & itLayerRoot ? _(L("Height ranges")) : _(L("Settings for height range")); + og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range"); update_and_show_layers = true; if (type & itLayer) @@ -2815,7 +2812,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay const int obj_idx = m_selected_object_id; if (obj_idx < 0) return false; - take_snapshot(_(L("Edit Height Range"))); + take_snapshot(_L("Edit Height Range")); const ItemType sel_type = m_objects_model->GetItemType(GetSelection()); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index cba7cef36c..095a926ad5 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -26,21 +26,26 @@ const double ObjectManipulation::mm_to_in = 0.0393700787; // Helper function to be used by drop to bed button. Returns lowest point of this // volume in world coordinate system. -static double get_volume_min_z(const GLVolume* volume) +static double get_volume_min_z(const GLVolume& volume) { - const Transform3f& world_matrix = volume->world_matrix().cast(); +#if ENABLE_ALLOW_NEGATIVE_Z + return volume.transformed_convex_hull_bounding_box().min.z(); +#else + const Transform3f& world_matrix = volume.world_matrix().cast(); // need to get the ModelVolume pointer - const ModelObject* mo = wxGetApp().model().objects[volume->composite_id.object_id]; - const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id]; + const ModelObject* mo = wxGetApp().model().objects[volume.composite_id.object_id]; + const ModelVolume* mv = mo->volumes[volume.composite_id.volume_id]; const TriangleMesh& hull = mv->get_convex_hull(); float min_z = std::numeric_limits::max(); for (const stl_facet& facet : hull.stl.facet_start) { - for (int i = 0; i < 3; ++ i) + for (int i = 0; i < 3; ++i) min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i])); } + return min_z; +#endif // ENABLE_ALLOW_NEGATIVE_Z } @@ -341,13 +346,27 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); - Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume)); + const Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(*volume)); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); change_position_value(0, diff.x()); change_position_value(1, diff.y()); change_position_value(2, diff.z()); } +#if ENABLE_ALLOW_NEGATIVE_Z + else if (selection.is_single_full_instance()) { + const ModelObjectPtrs& objects = wxGetApp().model().objects; + const int idx = selection.get_object_idx(); + if (0 <= idx && idx < static_cast(objects.size())) { + const ModelObject* mo = wxGetApp().model().objects[idx]; + const double min_z = mo->bounding_box().min.z(); + if (std::abs(min_z) > EPSILON) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); + change_position_value(2, m_cache.position.z() - min_z); + } + } + } +#endif // ENABLE_ALLOW_NEGATIVE_Z }); editors_grid_sizer->Add(m_drop_to_bed_button); @@ -671,11 +690,15 @@ void ObjectManipulation::update_reset_buttons_visibility() if (selection.is_single_full_instance()) { rotation = volume->get_instance_rotation(); scale = volume->get_instance_scaling_factor(); +#if ENABLE_ALLOW_NEGATIVE_Z + min_z = wxGetApp().model().objects[volume->composite_id.object_id]->bounding_box().min.z(); +#endif // ENABLE_ALLOW_NEGATIVE_Z + } else { rotation = volume->get_volume_rotation(); scale = volume->get_volume_scaling_factor(); - min_z = get_volume_min_z(volume); + min_z = get_volume_min_z(*volume); } show_rotation = !rotation.isApprox(Vec3d::Zero()); show_scale = !scale.isApprox(Vec3d::Ones()); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b2f335a54e..24d32b116d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1593,7 +1593,11 @@ struct Plater::priv BoundingBox scaled_bed_shape_bb() const; std::vector load_files(const std::vector& input_files, bool load_model, bool load_config, bool used_inches = false); +#if ENABLE_ALLOW_NEGATIVE_Z + std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false); +#else std::vector load_model_objects(const ModelObjectPtrs &model_objects); +#endif // ENABLE_ALLOW_NEGATIVE_Z wxString get_export_file(GUI::FileType file_type); const Selection& get_selection() const; @@ -2333,11 +2337,19 @@ std::vector Plater::priv::load_files(const std::vector& input_ return obj_idxs; } +#if ENABLE_ALLOW_NEGATIVE_Z + for (ModelObject* model_object : model.objects) { + if (!type_3mf && !type_zip_amf) + model_object->center_around_origin(false); + model_object->ensure_on_bed(is_project_file); + } +#else for (ModelObject* model_object : model.objects) { if (!type_3mf && !type_zip_amf) model_object->center_around_origin(false); model_object->ensure_on_bed(); } +#endif // ENABLE_ALLOW_NEGATIVE_Z // check multi-part object adding for the SLA-printing if (printer_technology == ptSLA) { @@ -2351,7 +2363,11 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (one_by_one) { +#if ENABLE_ALLOW_NEGATIVE_Z + auto loaded_idxs = load_model_objects(model.objects, is_project_file); +#else auto loaded_idxs = load_model_objects(model.objects); +#endif // ENABLE_ALLOW_NEGATIVE_Z obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } else { // This must be an .stl or .obj file, which may contain a maximum of one volume. @@ -2403,7 +2419,11 @@ std::vector Plater::priv::load_files(const std::vector& input_ // #define AUTOPLACEMENT_ON_LOAD +#if ENABLE_ALLOW_NEGATIVE_Z +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z) +#else std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) +#endif // ENABLE_ALLOW_NEGATIVE_Z { const BoundingBoxf bed_shape = bed_shape_bb(); const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); @@ -2480,7 +2500,11 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } #endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG +#if ENABLE_ALLOW_NEGATIVE_Z + object->ensure_on_bed(allow_negative_z); +#else object->ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z } #ifdef AUTOPLACEMENT_ON_LOAD @@ -2636,8 +2660,7 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { // if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running - bool enable_layer_editing = layers_height_allowed(); - if (!enable_layer_editing && view3D->is_layers_editing_enabled()) { + if (!layers_height_allowed() && view3D->is_layers_editing_enabled()) { SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING); on_action_layersediting(evt); } @@ -3078,21 +3101,19 @@ void Plater::priv::reload_from_disk() int volume_idx; // operators needed by std::algorithms - bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); } - bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); } + bool operator < (const SelectedVolume& other) const { return object_idx < other.object_idx || (object_idx == other.object_idx && volume_idx < other.volume_idx); } + bool operator == (const SelectedVolume& other) const { return object_idx == other.object_idx && volume_idx == other.volume_idx; } }; std::vector selected_volumes; // collects selected ModelVolumes const std::set& selected_volumes_idxs = selection.get_volume_idxs(); - for (unsigned int idx : selected_volumes_idxs) - { + for (unsigned int idx : selected_volumes_idxs) { const GLVolume* v = selection.get_volume(idx); int v_idx = v->volume_idx(); - if (v_idx >= 0) - { + if (v_idx >= 0) { int o_idx = v->object_idx(); - if ((0 <= o_idx) && (o_idx < (int)model.objects.size())) + if (0 <= o_idx && o_idx < (int)model.objects.size()) selected_volumes.push_back({ o_idx, v_idx }); } } @@ -3102,13 +3123,11 @@ void Plater::priv::reload_from_disk() // collects paths of files to load std::vector input_paths; std::vector missing_input_paths; - for (const SelectedVolume& v : selected_volumes) - { + for (const SelectedVolume& v : selected_volumes) { const ModelObject* object = model.objects[v.object_idx]; const ModelVolume* volume = object->volumes[v.volume_idx]; - if (!volume->source.input_file.empty()) - { + if (!volume->source.input_file.empty()) { if (fs::exists(volume->source.input_file)) input_paths.push_back(volume->source.input_file); else @@ -3121,8 +3140,7 @@ void Plater::priv::reload_from_disk() std::sort(missing_input_paths.begin(), missing_input_paths.end()); missing_input_paths.erase(std::unique(missing_input_paths.begin(), missing_input_paths.end()), missing_input_paths.end()); - while (!missing_input_paths.empty()) - { + while (!missing_input_paths.empty()) { // ask user to select the missing file fs::path search = missing_input_paths.back(); wxString title = _L("Please select the file to reload"); @@ -3136,21 +3154,18 @@ void Plater::priv::reload_from_disk() std::string sel_filename_path = dialog.GetPath().ToUTF8().data(); std::string sel_filename = fs::path(sel_filename_path).filename().string(); - if (boost::algorithm::iequals(search.filename().string(), sel_filename)) - { + if (boost::algorithm::iequals(search.filename().string(), sel_filename)) { input_paths.push_back(sel_filename_path); missing_input_paths.pop_back(); fs::path sel_path = fs::path(sel_filename_path).remove_filename().string(); std::vector::iterator it = missing_input_paths.begin(); - while (it != missing_input_paths.end()) - { + while (it != missing_input_paths.end()) { // try to use the path of the selected file with all remaining missing files fs::path repathed_filename = sel_path; repathed_filename /= it->filename(); - if (fs::exists(repathed_filename)) - { + if (fs::exists(repathed_filename)) { input_paths.push_back(repathed_filename.string()); it = missing_input_paths.erase(it); } @@ -3158,8 +3173,7 @@ void Plater::priv::reload_from_disk() ++it; } } - else - { + else { wxString message = _L("It is not allowed to change the file to reload") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to retry") + " ?"; wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION); if (dlg.ShowModal() != wxID_YES) @@ -3173,8 +3187,7 @@ void Plater::priv::reload_from_disk() std::vector fail_list; // load one file at a time - for (size_t i = 0; i < input_paths.size(); ++i) - { + for (size_t i = 0; i < input_paths.size(); ++i) { const auto& path = input_paths[i].string(); wxBusyCursor wait; @@ -3184,8 +3197,7 @@ void Plater::priv::reload_from_disk() try { new_model = Model::read_from_file(path, nullptr, true, false); - for (ModelObject* model_object : new_model.objects) - { + for (ModelObject* model_object : new_model.objects) { model_object->center_around_origin(); model_object->ensure_on_bed(); } @@ -3197,34 +3209,31 @@ void Plater::priv::reload_from_disk() } // update the selected volumes whose source is the current file - for (const SelectedVolume& sel_v : selected_volumes) - { + for (const SelectedVolume& sel_v : selected_volumes) { ModelObject* old_model_object = model.objects[sel_v.object_idx]; ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx]; +#if ENABLE_ALLOW_NEGATIVE_Z + bool sinking = old_model_object->bounding_box().min.z() < 0.0; +#endif // ENABLE_ALLOW_NEGATIVE_Z + bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string()); bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string()); - if (has_source || has_name) - { + if (has_source || has_name) { int new_volume_idx = -1; int new_object_idx = -1; - if (has_source) - { + if (has_source) { // take idxs from source new_volume_idx = old_volume->source.volume_idx; new_object_idx = old_volume->source.object_idx; } - else - { + else { // take idxs from the 1st matching volume - for (size_t o = 0; o < new_model.objects.size(); ++o) - { + for (size_t o = 0; o < new_model.objects.size(); ++o) { ModelObject* obj = new_model.objects[o]; bool found = false; - for (size_t v = 0; v < obj->volumes.size(); ++v) - { - if (obj->volumes[v]->name == old_volume->name) - { + for (size_t v = 0; v < obj->volumes.size(); ++v) { + if (obj->volumes[v]->name == old_volume->name) { new_volume_idx = (int)v; new_object_idx = (int)o; found = true; @@ -3236,19 +3245,16 @@ void Plater::priv::reload_from_disk() } } - if ((new_object_idx < 0) && ((int)new_model.objects.size() <= new_object_idx)) - { + if (new_object_idx < 0 && (int)new_model.objects.size() <= new_object_idx) { fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); continue; } ModelObject* new_model_object = new_model.objects[new_object_idx]; - if ((new_volume_idx < 0) && ((int)new_model.objects.size() <= new_volume_idx)) - { + if (new_volume_idx < 0 && (int)new_model.objects.size() <= new_volume_idx) { fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); continue; } - if (new_volume_idx < (int)new_model_object->volumes.size()) - { + if (new_volume_idx < (int)new_model_object->volumes.size()) { old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]); ModelVolume* new_volume = old_model_object->volumes.back(); new_volume->set_new_unique_id(); @@ -3265,7 +3271,10 @@ void Plater::priv::reload_from_disk() new_volume->seam_facets.assign(old_volume->seam_facets); std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); - old_model_object->ensure_on_bed(); +#if ENABLE_ALLOW_NEGATIVE_Z + if (!sinking) +#endif // ENABLE_ALLOW_NEGATIVE_Z + old_model_object->ensure_on_bed(); sla::reproject_points_and_holes(old_model_object); } @@ -3273,11 +3282,9 @@ void Plater::priv::reload_from_disk() } } - if (!fail_list.empty()) - { + if (!fail_list.empty()) { wxString message = _L("Unable to reload:") + "\n"; - for (const wxString& s : fail_list) - { + for (const wxString& s : fail_list) { message += s + "\n"; } wxMessageDialog dlg(q, message, _L("Error during reload"), wxOK | wxOK_DEFAULT | wxICON_WARNING); @@ -3288,8 +3295,7 @@ void Plater::priv::reload_from_disk() update(); // new GLVolumes have been created at this point, so update their printable state - for (size_t i = 0; i < model.objects.size(); ++i) - { + for (size_t i = 0; i < model.objects.size(); ++i) { view3D->get_canvas3d()->update_instance_printable_state_for_object(i); } } @@ -3309,8 +3315,7 @@ void Plater::priv::reload_all_from_disk() reload_from_disk(); // restore previous selection selection.clear(); - for (unsigned int idx : curr_idxs) - { + for (unsigned int idx : curr_idxs) { selection.add(idx, false); } } @@ -4030,7 +4035,7 @@ void Plater::priv::reset_gcode_toolpaths() bool Plater::priv::can_set_instance_to_object() const { const int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->instances.size() > 1; } bool Plater::priv::can_split(bool to_objects) const @@ -4044,7 +4049,12 @@ bool Plater::priv::layers_height_allowed() const return false; int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); +#if ENABLE_ALLOW_NEGATIVE_Z + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->bounding_box().max.z() > 0.0 && + config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); +#else + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z } bool Plater::priv::can_mirror() const @@ -5923,6 +5933,14 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology) //FIXME for SLA synchronize //p->background_process.apply(Model)! +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (printer_technology == ptSLA) { + for (ModelObject* model_object : p->model.objects) { + model_object->ensure_on_bed(); + } + } +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export"); p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer"); @@ -5942,7 +5960,15 @@ void Plater::changed_object(int obj_idx) return; // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; +#if ENABLE_ALLOW_NEGATIVE_Z +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + model_object->ensure_on_bed(this->p->printer_technology != ptSLA); +#else + model_object->ensure_on_bed(true); +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA +#else model_object->ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data, update the 3D scene. @@ -5960,11 +5986,18 @@ void Plater::changed_objects(const std::vector& object_idxs) if (object_idxs.empty()) return; - for (size_t obj_idx : object_idxs) - { + for (size_t obj_idx : object_idxs) { +#if ENABLE_ALLOW_NEGATIVE_Z + if (obj_idx < p->model.objects.size()) { + if (p->model.objects[obj_idx]->bounding_box().min.z() >= 0.0) + // re - align to Z = 0 + p->model.objects[obj_idx]->ensure_on_bed(); + } +#else if (obj_idx < p->model.objects.size()) // recenter and re - align to Z = 0 p->model.objects[obj_idx]->ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index fd68737493..4cf71363d8 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -12,6 +12,9 @@ #include "Plater.hpp" #include "libslic3r/Model.hpp" +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA +#include "libslic3r/PresetBundle.hpp" +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA #include @@ -58,13 +61,11 @@ bool Selection::Clipboard::is_sla_compliant() const if (m_mode == Selection::Volume) return false; - for (const ModelObject* o : m_model->objects) - { + for (const ModelObject* o : m_model->objects) { if (o->is_multiparts()) return false; - for (const ModelVolume* v : o->volumes) - { + for (const ModelVolume* v : o->volumes) { if (v->is_modifier()) return false; } @@ -78,7 +79,8 @@ Selection::Clipboard::Clipboard() m_model.reset(new Model); } -void Selection::Clipboard::reset() { +void Selection::Clipboard::reset() +{ m_model->clear_objects(); } @@ -149,7 +151,7 @@ void Selection::set_model(Model* model) void Selection::add(unsigned int volume_idx, bool as_single_selection, bool check_for_already_contained) { - if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + if (!m_valid || (unsigned int)m_volumes->size() <= volume_idx) return; const GLVolume* volume = (*m_volumes)[volume_idx]; @@ -167,9 +169,8 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec needs_reset |= as_single_selection && !is_any_modifier() && volume->is_modifier; needs_reset |= is_any_modifier() && !volume->is_modifier; - if (!already_contained || needs_reset) - { - wxGetApp().plater()->take_snapshot(_(L("Selection-Add"))); + if (!already_contained || needs_reset) { + wxGetApp().plater()->take_snapshot(_L("Selection-Add")); if (needs_reset) clear(); @@ -185,7 +186,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec { case Volume: { - if ((volume->volume_idx() >= 0) && (is_empty() || (volume->instance_idx() == get_instance_idx()))) + if (volume->volume_idx() >= 0 && (is_empty() || volume->instance_idx() == get_instance_idx())) do_add_volume(volume_idx); break; @@ -204,13 +205,13 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec void Selection::remove(unsigned int volume_idx) { - if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + if (!m_valid || (unsigned int)m_volumes->size() <= volume_idx) return; if (!contains_volume(volume_idx)) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove")); GLVolume* volume = (*m_volumes)[volume_idx]; @@ -242,7 +243,7 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection) (as_single_selection && matches(volume_idxs))) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Add Object"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Add Object")); // resets the current list if needed if (as_single_selection) @@ -261,7 +262,7 @@ void Selection::remove_object(unsigned int object_idx) if (!m_valid) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Object"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object")); do_remove_object(object_idx); @@ -274,12 +275,12 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, if (!m_valid) return; - std::vector volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx); + const std::vector volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx); if ((!as_single_selection && contains_all_volumes(volume_idxs)) || (as_single_selection && matches(volume_idxs))) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Add Instance"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance")); // resets the current list if needed if (as_single_selection) @@ -298,7 +299,7 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i if (!m_valid) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Instance"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance")); do_remove_instance(object_idx, instance_idx); @@ -333,10 +334,9 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) if (!m_valid) return; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) + if (v->object_idx() == (int)object_idx && v->volume_idx() == (int)volume_idx) do_remove_volume(i); } @@ -358,8 +358,7 @@ void Selection::add_volumes(EMode mode, const std::vector& volume_ clear(); m_mode = mode; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (i < (unsigned int)m_volumes->size()) do_add_volume(i); } @@ -374,8 +373,7 @@ void Selection::remove_volumes(EMode mode, const std::vector& volu return; m_mode = mode; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (i < (unsigned int)m_volumes->size()) do_remove_volume(i); } @@ -390,8 +388,7 @@ void Selection::add_all() return; unsigned int count = 0; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { if (!(*m_volumes)[i]->is_wipe_tower) ++count; } @@ -404,8 +401,7 @@ void Selection::add_all() m_mode = Instance; clear(); - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { if (!(*m_volumes)[i]->is_wipe_tower) do_add_volume(i); } @@ -455,8 +451,7 @@ void Selection::clear() if (m_list.empty()) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { (*m_volumes)[i]->selected = false; } @@ -522,16 +517,15 @@ bool Selection::is_single_full_instance() const return false; int object_idx = m_valid ? get_object_idx() : -1; - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) return false; int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx(); std::set volumes_idxs; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; - if ((object_idx != v->object_idx()) || (instance_idx != v->instance_idx())) + if (object_idx != v->object_idx() || instance_idx != v->instance_idx()) return false; int volume_idx = v->volume_idx(); @@ -544,8 +538,8 @@ bool Selection::is_single_full_instance() const bool Selection::is_from_single_object() const { - int idx = get_object_idx(); - return (0 <= idx) && (idx < 1000); + const int idx = get_object_idx(); + return 0 <= idx && idx < 1000; } bool Selection::is_sla_compliant() const @@ -553,8 +547,7 @@ bool Selection::is_sla_compliant() const if (m_mode == Volume) return false; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if ((*m_volumes)[i]->is_modifier) return false; } @@ -564,8 +557,7 @@ bool Selection::is_sla_compliant() const bool Selection::contains_all_volumes(const std::vector& volume_idxs) const { - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) == m_list.end()) return false; } @@ -575,8 +567,7 @@ bool Selection::contains_all_volumes(const std::vector& volume_idx bool Selection::contains_any_volume(const std::vector& volume_idxs) const { - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) != m_list.end()) return true; } @@ -588,8 +579,7 @@ bool Selection::matches(const std::vector& volume_idxs) const { unsigned int count = 0; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) != m_list.end()) ++count; else @@ -614,8 +604,7 @@ int Selection::get_object_idx() const int Selection::get_instance_idx() const { - if (m_cache.content.size() == 1) - { + if (m_cache.content.size() == 1) { const InstanceIdxsList& idxs = m_cache.content.begin()->second; if (idxs.size() == 1) return *idxs.begin(); @@ -672,25 +661,20 @@ void Selection::translate(const Vec3d& displacement, bool local) EMode translation_type = m_mode; - for (unsigned int i : m_list) - { - if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) - { + for (unsigned int i : m_list) { + if (m_mode == Volume || (*m_volumes)[i]->is_wipe_tower) { if (local) (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); - else - { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + else { + const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); } } - else if (m_mode == Instance) - { + else if (m_mode == Instance) { if (is_from_fully_selected_instance(i)) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); - else - { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + else { + const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); translation_type = Volume; } @@ -718,18 +702,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ if (!is_wipe_tower()) { int rot_axis_max = 0; - if (rotation.isApprox(Vec3d::Zero())) - { - for (unsigned int i : m_list) - { + if (rotation.isApprox(Vec3d::Zero())) { + for (unsigned int i : m_list) { GLVolume &volume = *(*m_volumes)[i]; - if (m_mode == Instance) - { + if (m_mode == Instance) { volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); } - else if (m_mode == Volume) - { + else if (m_mode == Volume) { volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); } @@ -746,14 +726,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it. std::vector object_instance_first(m_model->objects.size(), -1); auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) { - int first_volume_idx = object_instance_first[volume.object_idx()]; + const int first_volume_idx = object_instance_first[volume.object_idx()]; if (rot_axis_max != 2 && first_volume_idx != -1) { // Generic rotation, but no rotation around the Z axis. // Always do a local rotation (do not consider the selection to be a rigid body). assert(is_approx(rotation.z(), 0.0)); const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; const Vec3d &rotation = first_volume.get_instance_rotation(); - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); } else { @@ -763,7 +743,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); if (rot_axis_max == 2 && transformation_type.joint()) { // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); } volume.set_instance_rotation(new_rotation); @@ -771,19 +751,16 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ } }; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume &volume = *(*m_volumes)[i]; if (is_single_full_instance()) rotate_instance(volume, i); - else if (is_single_volume() || is_single_modifier()) - { + else if (is_single_volume() || is_single_modifier()) { if (transformation_type.independent()) volume.set_volume_rotation(volume.get_volume_rotation() + rotation); - else - { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + else { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); volume.set_volume_rotation(new_rotation); } } @@ -791,15 +768,13 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ { if (m_mode == Instance) rotate_instance(volume, i); - else if (m_mode == Volume) - { + else if (m_mode == Volume) { // extracts rotations from the composed transformation Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - if (transformation_type.joint()) - { - Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; - Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); + if (transformation_type.joint()) { + const Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; + const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); volume.set_volume_offset(local_pivot + offset); } volume.set_volume_rotation(new_rotation); @@ -820,8 +795,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // make sure the wipe tower rotates around its center, not origin // we can assume that only Z rotation changes - Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset(); - Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0, 0, 1)) * center_local; + const Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset(); + const Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0.0, 0.0, 1.0)) * center_local; volume.set_volume_rotation(rotation); volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new); } @@ -839,8 +814,7 @@ void Selection::flattening_rotate(const Vec3d& normal) if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { // Normal transformed from the object coordinate space to the world coordinate space. const auto &voldata = m_cache.volumes_data[i]; Vec3d tnormal = (Geometry::assemble_transform( @@ -866,12 +840,10 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume &volume = *(*m_volumes)[i]; if (is_single_full_instance()) { - if (transformation_type.relative()) - { + if (transformation_type.relative()) { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation @@ -881,8 +853,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type volume.set_instance_scaling_factor(new_scale); } - else - { + else { if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) { // Non-uniform scaling. Transform the scaling factors into the local coordinate system. // This is only possible, if the instance rotation is mulitples of ninety degrees. @@ -895,11 +866,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type } else if (is_single_volume() || is_single_modifier()) volume.set_volume_scaling_factor(scale); - else - { + else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); - if (m_mode == Instance) - { + if (m_mode == Instance) { Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); @@ -908,13 +877,11 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type volume.set_instance_scaling_factor(new_scale); } - else if (m_mode == Volume) - { + else if (m_mode == Volume) { Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - if (transformation_type.joint()) - { + if (transformation_type.joint()) { Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); } @@ -929,35 +896,36 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type else if (m_mode == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH - - ensure_on_bed(); + +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) + ensure_on_bed(); +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA this->set_bounding_boxes_dirty(); } void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) { - if (is_empty() || (m_mode == Volume)) + if (is_empty() || m_mode == Volume) return; // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings Vec3d box_size = get_bounding_box().size() + 0.01 * Vec3d::Ones(); const ConfigOptionPoints* opt = dynamic_cast(config.option("bed_shape")); - if (opt != nullptr) - { + if (opt != nullptr) { BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config.opt_float("max_print_height"))); + BoundingBoxf3 print_volume({ unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0 }, { unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config.opt_float("max_print_height") }); Vec3d print_volume_size = print_volume.size(); double sx = (box_size(0) != 0.0) ? print_volume_size(0) / box_size(0) : 0.0; double sy = (box_size(1) != 0.0) ? print_volume_size(1) / box_size(1) : 0.0; double sz = (box_size(2) != 0.0) ? print_volume_size(2) / box_size(2) : 0.0; - if ((sx != 0.0) && (sy != 0.0) && (sz != 0.0)) + if (sx != 0.0 && sy != 0.0 && sz != 0.0) { double s = std::min(sx, std::min(sy, sz)); - if (s != 1.0) - { - wxGetApp().plater()->take_snapshot(_(L("Scale To Fit"))); + if (s != 1.0) { + wxGetApp().plater()->take_snapshot(_L("Scale To Fit")); TransformationType type; type.set_world(); @@ -987,8 +955,7 @@ void Selection::mirror(Axis axis) bool single_full_instance = is_single_full_instance(); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (single_full_instance) (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); else if (m_mode == Volume) @@ -1010,8 +977,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == (int)object_idx) v->set_instance_offset(v->get_instance_offset() + displacement); @@ -1020,8 +986,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (done.size() == m_volumes->size()) break; @@ -1030,8 +995,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) continue; // Process unselected volumes of the object. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (done.size() == m_volumes->size()) break; @@ -1055,18 +1019,16 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) + if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) v->set_instance_offset(v->get_instance_offset() + displacement); } std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (done.size() == m_volumes->size()) break; @@ -1075,8 +1037,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co continue; // Process unselected volumes of the object. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (done.size() == m_volumes->size()) break; @@ -1084,7 +1045,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() != (int)instance_idx)) + if (v->object_idx() != object_idx || v->instance_idx() != (int)instance_idx) continue; v->set_instance_offset(v->get_instance_offset() + displacement); @@ -1799,18 +1760,16 @@ void Selection::render_synchronized_volumes() const float color[3] = { 1.0f, 1.0f, 0.0f }; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* volume = (*m_volumes)[i]; int object_idx = volume->object_idx(); int volume_idx = volume->volume_idx(); - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (i == j) continue; const GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + if (v->object_idx() != object_idx || v->volume_idx() != volume_idx) continue; render_bounding_box(v->transformed_convex_hull_bounding_box(), color); @@ -2032,9 +1991,9 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co #ifndef NDEBUG static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { - Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); - Vec3d axis = angle_axis.axis(); - double angle = angle_axis.angle(); + const Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + const Vec3d axis = angle_axis.axis(); + const double angle = angle_axis.angle(); if (std::abs(angle) < 1e-8) return true; assert(std::abs(axis.x()) < 1e-8); @@ -2071,24 +2030,22 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (done.size() == m_volumes->size()) break; const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); + const int object_idx = volume->object_idx(); if (object_idx >= 1000) continue; - int instance_idx = volume->instance_idx(); + const int instance_idx = volume->instance_idx(); const Vec3d& rotation = volume->get_instance_rotation(); const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); const Vec3d& mirror = volume->get_instance_mirror(); // Process unselected instances. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (done.size() == m_volumes->size()) break; @@ -2096,24 +2053,36 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) + if (v->object_idx() != object_idx || v->instance_idx() == instance_idx) continue; assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation())); switch (sync_rotation_type) { - case SYNC_ROTATION_NONE: + case SYNC_ROTATION_NONE: { +#if ENABLE_ALLOW_NEGATIVE_Z + // z only rotation -> synch instance z + // The X,Y rotations should be synchronized from start to end of the rotation. + assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + v->set_instance_offset(Z, volume->get_instance_offset().z()); + break; +#else // z only rotation -> keep instance z // The X,Y rotations should be synchronized from start to end of the rotation. assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); break; +#endif // ENABLE_ALLOW_NEGATIVE_Z + } case SYNC_ROTATION_FULL: // rotation comes from place on face -> force given z - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2))); + v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() }); break; case SYNC_ROTATION_GENERAL: // generic rotation -> update instance z with the delta of the rotation. - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); + v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() + z_diff }); break; } @@ -2131,27 +2100,25 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ void Selection::synchronize_unselected_volumes() { - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); + const int object_idx = volume->object_idx(); if (object_idx >= 1000) continue; - int volume_idx = volume->volume_idx(); + const int volume_idx = volume->volume_idx(); const Vec3d& offset = volume->get_volume_offset(); const Vec3d& rotation = volume->get_volume_rotation(); const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); const Vec3d& mirror = volume->get_volume_mirror(); // Process unselected volumes. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (j == i) continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + if (v->object_idx() != object_idx || v->volume_idx() != volume_idx) continue; v->set_volume_offset(offset); @@ -2167,10 +2134,8 @@ void Selection::ensure_on_bed() typedef std::map, double> InstancesToZMap; InstancesToZMap instances_min_z; - for (GLVolume* volume : *m_volumes) - { - if (!volume->is_wipe_tower && !volume->is_modifier) - { + for (GLVolume* volume : *m_volumes) { + if (!volume->is_wipe_tower && !volume->is_modifier) { double min_z = volume->transformed_convex_hull_bounding_box().min(2); std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); InstancesToZMap::iterator it = instances_min_z.find(instance); @@ -2181,8 +2146,7 @@ void Selection::ensure_on_bed() } } - for (GLVolume* volume : *m_volumes) - { + for (GLVolume* volume : *m_volumes) { std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); InstancesToZMap::iterator it = instances_min_z.find(instance); if (it != instances_min_z.end()) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 8bb418baac..c28a6e867b 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -129,7 +129,7 @@ private: TransformCache m_instance; public: - VolumeCache() {} + VolumeCache() = default; VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); const Vec3d& get_volume_position() const { return m_volume.position; }