diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 716e61a60..b83f7638b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -66,6 +66,8 @@ // 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) +// Enable drawing contours, at cut level, for sinking volumes +#define ENABLE_SINKING_CONTOURS (1 && ENABLE_ALLOW_NEGATIVE_Z) // Enable delayed rendering of transparent volumes #define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA0) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index fb464ce02..7e5b2c056 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -9,9 +9,9 @@ #include "3DScene.hpp" #include "GLShader.hpp" #include "GUI_App.hpp" -#if ENABLE_ENVIRONMENT_MAP +#if ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS #include "Plater.hpp" -#endif // ENABLE_ENVIRONMENT_MAP +#endif // ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -286,6 +286,117 @@ void GLIndexedVertexArray::render( glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } +#if ENABLE_SINKING_CONTOURS +#define ALG_SLICE_MESH 1 +#define ALG_SLICE_MESHEX 2 +#define ALG_SLICE ALG_SLICE_MESH +void GLVolume::SinkingContours::update() +{ + if (m_parent.is_sinking() && !m_parent.is_below_printbed()) { + const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); + if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { + m_old_box = box; + m_shift = Vec3d::Zero(); + + const TriangleMesh& mesh = GUI::wxGetApp().plater()->model().objects[m_parent.object_idx()]->volumes[m_parent.volume_idx()]->mesh(); + assert(mesh.has_shared_vertices()); + +#if ALG_SLICE == ALG_SLICE_MESH + MeshSlicingParams slicing_params; + slicing_params.trafo = m_parent.world_matrix(); + std::vector list_of_polys = slice_mesh(mesh.its, std::vector{ 0.0f }, slicing_params); + + auto append_polygon = [this](const Polygon& polygon, GUI::GLModel::InitializationData& data) { + if (!polygon.empty()) { + GUI::GLModel::InitializationData::Entity entity; + entity.type = GUI::GLModel::PrimitiveType::LineLoop; + entity.color[0] = 1.0f - m_parent.render_color[0]; + entity.color[1] = 1.0f - m_parent.render_color[1]; + entity.color[2] = 1.0f - m_parent.render_color[2]; + entity.color[3] = m_parent.render_color[3]; + // contour + entity.positions.reserve(polygon.size() + 1); + entity.indices.reserve(polygon.size() + 1); + unsigned int id = 0; + for (const Point& p : polygon) { + entity.positions.emplace_back(unscale(p.x(), p.y(), 0.0).cast()); + entity.indices.emplace_back(id++); + } + data.entities.emplace_back(entity); + } + }; + + m_model.reset(); + GUI::GLModel::InitializationData init_data; + for (const Polygons& polygons : list_of_polys) { + for (const Polygon& polygon : polygons) { + // contour + append_polygon(polygon, init_data); + } + } +#else + MeshSlicingParamsEx slicing_params; + slicing_params.trafo = m_parent.world_matrix(); + std::vector list_of_expolys = slice_mesh_ex(mesh.its, std::vector{ 0.0f }, slicing_params); + + auto append_polygon = [this](const Polygon& polygon, GUI::GLModel::InitializationData& data) { + if (!polygon.empty()) { + GUI::GLModel::InitializationData::Entity entity; + entity.type = GUI::GLModel::PrimitiveType::LineLoop; + entity.color[0] = 1.0f - m_parent.render_color[0]; + entity.color[1] = 1.0f - m_parent.render_color[1]; + entity.color[2] = 1.0f - m_parent.render_color[2]; + entity.color[3] = m_parent.render_color[3]; + // contour + entity.positions.reserve(polygon.size() + 1); + entity.indices.reserve(polygon.size() + 1); + unsigned int id = 0; + for (const Point& p : polygon) { + entity.positions.emplace_back(unscale(p.x(), p.y(), 0.0).cast()); + entity.indices.emplace_back(id++); + } + data.entities.emplace_back(entity); + } + }; + + m_model.reset(); + GUI::GLModel::InitializationData init_data; + for (const ExPolygons& polygons : list_of_expolys) { + for (const ExPolygon& polygon : polygons) { + // contour + append_polygon(polygon.contour, init_data); + // holes + for (const Polygon& hole : polygon.holes) { + append_polygon(hole, init_data); + } + } + } +#endif // ALG_SLICE == ALG_SLICE_MESH + + if (!init_data.entities.empty()) + m_model.init_from(init_data); + } + else + m_shift = box.center() - m_old_box.center(); + } + else + m_model.reset(); +} + +void GLVolume::SinkingContours::set_color(const std::array& color) +{ + m_model.set_color(-1, { 1.0f - color[0], 1.0f - color[1], 1.0f - color[2], color[3] }); +} + +void GLVolume::SinkingContours::render() const +{ + glsafe(::glPushMatrix()); + glsafe(::glTranslated(m_shift.x(), m_shift.y(), m_shift.z())); + m_model.render(); + glsafe(::glPopMatrix()); +} +#endif // ENABLE_SINKING_CONTOURS + const std::array GLVolume::SELECTED_COLOR = { 0.0f, 1.0f, 0.0f, 1.0f }; const std::array GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f }; const std::array GLVolume::HOVER_DESELECT_COLOR = { 1.0f, 0.75f, 0.75f, 1.0f }; @@ -306,6 +417,9 @@ GLVolume::GLVolume(float r, float g, float b, float a) : m_transformed_bounding_box_dirty(true) , m_sla_shift_z(0.0) , m_transformed_convex_hull_bounding_box_dirty(true) +#if ENABLE_SINKING_CONTOURS + , m_sinking_contours(*this) +#endif // ENABLE_SINKING_CONTOURS // geometry_id == 0 -> invalid , geometry_id(std::pair(0, 0)) , extruder_id(0) @@ -537,6 +651,23 @@ bool GLVolume::is_below_printbed() const { return transformed_convex_hull_bounding_box().max(2) < 0.0; } + +#if ENABLE_SINKING_CONTOURS +void GLVolume::update_sinking_contours() +{ + m_sinking_contours.update(); +} + +void GLVolume::update_sinking_contours_color() +{ + m_sinking_contours.set_color(render_color); +} + +void GLVolume::render_sinking_contours() +{ + m_sinking_contours.render(); +} +#endif // ENABLE_SINKING_CONTOURS #endif // ENABLE_ALLOW_NEGATIVE_Z std::vector GLVolumeCollection::load_object( @@ -774,6 +905,70 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab if (disable_cullface) glsafe(::glDisable(GL_CULL_FACE)); +#if ENABLE_SINKING_CONTOURS + GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func); + for (GLVolumeWithIdAndZ& volume : to_render) { + volume.first->set_render_color(); + + // render sinking contours of non-hovered volumes + if (volume.first->is_sinking() && !volume.first->is_below_printbed() && volume.first->hover == GLVolume::HS_None) { + shader->stop_using(); + glsafe(::glLineWidth(5.0f)); + volume.first->update_sinking_contours_color(); + volume.first->render_sinking_contours(); + shader->start_using(); + } + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + shader->set_uniform("uniform_color", volume.first->render_color); + shader->set_uniform("z_range", m_z_range, 2); + shader->set_uniform("clipping_plane", m_clipping_plane, 4); + shader->set_uniform("print_box.min", m_print_box_min, 3); + shader->set_uniform("print_box.max", m_print_box_max, 3); + shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled); + 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())); + shader->set_uniform("slope.normal_z", m_slope.normal_z); + +#if ENABLE_ENVIRONMENT_MAP + unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id(); + bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1"; + shader->set_uniform("use_environment_tex", use_environment_texture); + if (use_environment_texture) + glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id)); +#endif // ENABLE_ENVIRONMENT_MAP + glcheck(); + + volume.first->render(); + +#if ENABLE_ENVIRONMENT_MAP + if (use_environment_texture) + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); +#endif // ENABLE_ENVIRONMENT_MAP + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + } + + for (GLVolumeWithIdAndZ& volume : to_render) { + // render sinking contours of hovered volumes + if (volume.first->is_sinking() && !volume.first->is_below_printbed() && volume.first->hover != GLVolume::HS_None) { + shader->stop_using(); + glsafe(::glLineWidth(5.0f)); + glsafe(::glDisable(GL_DEPTH_TEST)); + volume.first->update_sinking_contours_color(); + volume.first->render_sinking_contours(); + glsafe(::glEnable(GL_DEPTH_TEST)); + shader->start_using(); + } + } +#else glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); @@ -813,6 +1008,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); +#endif // ENABLE_SINKING_CONTOURS if (disable_cullface) glsafe(::glEnable(GL_CULL_FACE)); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 217d39746..36ea73a62 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -8,6 +8,10 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Geometry.hpp" +#if ENABLE_SINKING_CONTOURS +#include "GLModel.hpp" +#endif // ENABLE_SINKING_CONTOURS + #include #define HAS_GLSAFE @@ -250,6 +254,9 @@ public: enum EHoverState : unsigned char { HS_None, +#if ENABLE_SINKING_CONTOURS + HS_Hover, +#endif // ENABLE_SINKING_CONTOURS HS_Select, HS_Deselect }; @@ -262,7 +269,7 @@ private: Geometry::Transformation m_volume_transformation; // Shift in z required by sla supports+pad - double m_sla_shift_z; + double m_sla_shift_z; // Bounding box of this volume, in unscaled coordinates. BoundingBoxf3 m_transformed_bounding_box; // Whether or not is needed to recalculate the transformed bounding box. @@ -274,6 +281,24 @@ private: // Whether or not is needed to recalculate the transformed convex hull bounding box. bool m_transformed_convex_hull_bounding_box_dirty; +#if ENABLE_SINKING_CONTOURS + class SinkingContours + { + GLVolume& m_parent; + GUI::GLModel m_model; + BoundingBoxf3 m_old_box; + Vec3d m_shift{ Vec3d::Zero() }; + + public: + SinkingContours(GLVolume& volume) : m_parent(volume) {} + void update(); + void set_color(const std::array& color); + void render() const; + }; + + SinkingContours m_sinking_contours; +#endif // ENABLE_SINKING_CONTOURS + public: // Color of the triangles / quads held by this volume. std::array color; @@ -462,6 +487,11 @@ public: #if ENABLE_ALLOW_NEGATIVE_Z bool is_sinking() const; bool is_below_printbed() const; +#if ENABLE_SINKING_CONTOURS + void update_sinking_contours(); + void update_sinking_contours_color(); + void render_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS #endif // ENABLE_ALLOW_NEGATIVE_Z // Return an estimate of the memory consumed by this class. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5be2fc7ad..33ea49c9b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2063,6 +2063,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_gizmos.update_data(); m_gizmos.refresh_on_off_state(); +#if ENABLE_SINKING_CONTOURS + for (GLVolume* v : m_volumes.volumes) { + v->update_sinking_contours(); + } +#endif // ENABLE_SINKING_CONTOURS + // Update the toolbar if (update_object_list) post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); @@ -5639,6 +5645,11 @@ void GLCanvas3D::_update_volumes_hover_state() } } } +#if ENABLE_SINKING_CONTOURS + else if (volume.selected) + volume.hover = GLVolume::HS_Hover; +#endif // ENABLE_SINKING_CONTOURS + } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 55c7f371b..e42a10d61 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4750,6 +4750,7 @@ void Plater::render_project_state_debug_window() const { p->render_project_state #endif // ENABLE_PROJECT_DIRTY_STATE Sidebar& Plater::sidebar() { return *p->sidebar; } +const Model& Plater::model() const { return p->model; } Model& Plater::model() { return p->model; } const Print& Plater::fff_print() const { return p->fff_print; } Print& Plater::fff_print() { return p->fff_print; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index fc4001ba5..d4d774efe 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -149,6 +149,7 @@ public: #endif // ENABLE_PROJECT_DIRTY_STATE Sidebar& sidebar(); + const Model& model() const; Model& model(); const Print& fff_print() const; Print& fff_print(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index f2e879605..834ec69d7 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -676,6 +676,9 @@ void Selection::translate(const Vec3d& displacement, bool local) translation_type = Volume; } } +#if ENABLE_SINKING_CONTOURS + v.update_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS } #if !DISABLE_INSTANCES_SYNCH @@ -701,15 +704,18 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ int rot_axis_max = 0; if (rotation.isApprox(Vec3d::Zero())) { for (unsigned int i : m_list) { - GLVolume &volume = *(*m_volumes)[i]; + GLVolume &v = *(*m_volumes)[i]; 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()); + v.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); + v.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); } 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()); + v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); + v.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); } +#if ENABLE_SINKING_CONTOURS + v.update_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS } } else { // this is not the wipe tower @@ -749,22 +755,22 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ }; for (unsigned int i : m_list) { - GLVolume &volume = *(*m_volumes)[i]; + GLVolume &v = *(*m_volumes)[i]; if (is_single_full_instance()) - rotate_instance(volume, i); + rotate_instance(v, i); else if (is_single_volume() || is_single_modifier()) { if (transformation_type.independent()) - volume.set_volume_rotation(volume.get_volume_rotation() + rotation); + v.set_volume_rotation(v.get_volume_rotation() + rotation); 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); + v.set_volume_rotation(new_rotation); } } else { if (m_mode == Instance) - rotate_instance(volume, i); + rotate_instance(v, i); else if (m_mode == Volume) { // extracts rotations from the composed transformation Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); @@ -772,11 +778,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ 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); + v.set_volume_offset(local_pivot + offset); } - volume.set_volume_rotation(new_rotation); + v.set_volume_rotation(new_rotation); } } +#if ENABLE_SINKING_CONTOURS + v.update_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS } } @@ -821,6 +830,9 @@ void Selection::flattening_rotate(const Vec3d& normal) // Additional rotation to align tnormal with the down vector in the world coordinate space. auto extra_rotation = Eigen::Quaterniond().setFromTwoVectors(tnormal, - Vec3d::UnitZ()); v.set_instance_rotation(Geometry::extract_euler_angles(extra_rotation.toRotationMatrix() * m_cache.volumes_data[i].get_instance_rotation_matrix())); +#if ENABLE_SINKING_CONTOURS + v.update_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS } #if !DISABLE_INSTANCES_SYNCH @@ -846,12 +858,12 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type #endif // ENABLE_ALLOW_NEGATIVE_Z for (unsigned int i : m_list) { - GLVolume &volume = *(*m_volumes)[i]; + GLVolume &v = *(*m_volumes)[i]; #if ENABLE_ALLOW_NEGATIVE_Z #if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA if (!is_sla) #endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA - is_any_volume_sinking |= !volume.is_modifier && std::find(m_cache.sinking_volumes.begin(), m_cache.sinking_volumes.end(), i) != m_cache.sinking_volumes.end(); + is_any_volume_sinking |= !v.is_modifier && std::find(m_cache.sinking_volumes.begin(), m_cache.sinking_volumes.end(), i) != m_cache.sinking_volumes.end(); #endif // ENABLE_ALLOW_NEGATIVE_Z if (is_single_full_instance()) { if (transformation_type.relative()) { @@ -860,23 +872,23 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type // 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()) - volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); - volume.set_instance_scaling_factor(new_scale); + v.set_instance_scaling_factor(new_scale); } 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. - assert(Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation())); - volume.set_instance_scaling_factor((volume.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs()); + assert(Geometry::is_rotation_ninety_degrees(v.get_instance_rotation())); + v.set_instance_scaling_factor((v.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs()); } else - volume.set_instance_scaling_factor(scale); + v.set_instance_scaling_factor(scale); } } else if (is_single_volume() || is_single_modifier()) - volume.set_volume_scaling_factor(scale); + v.set_volume_scaling_factor(scale); else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); if (m_mode == Instance) { @@ -884,9 +896,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type // 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()) - volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); - volume.set_instance_scaling_factor(new_scale); + v.set_instance_scaling_factor(new_scale); } 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); @@ -894,11 +906,14 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); 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); + v.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); } - volume.set_volume_scaling_factor(new_scale); + v.set_volume_scaling_factor(new_scale); } } +#if ENABLE_SINKING_CONTOURS + v.update_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS } #if !DISABLE_INSTANCES_SYNCH @@ -972,6 +987,9 @@ void Selection::mirror(Axis axis) v.set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); else if (m_mode == Volume) v.set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); +#if ENABLE_SINKING_CONTOURS + v.update_sinking_contours(); +#endif // ENABLE_SINKING_CONTOURS } #if !DISABLE_INSTANCES_SYNCH