diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 2e0eae7322..6e752a90d7 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -300,7 +300,7 @@ namespace Emboss class ProjectZ : public IProjection { public: - ProjectZ(double depth) : m_depth(depth) {} + explicit ProjectZ(double depth) : m_depth(depth) {} // Inherited via IProject std::pair create_front_back(const Point &p) const override; Vec3d project(const Vec3d &point) const override; diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index a46422022b..37fadfacbd 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -150,11 +150,11 @@ inline bool has_reflection(const Transform3d &transform) { return transform.matr /// column index /// source transformation /// Base of transformation matrix -inline const Vec3d &get_base(unsigned index, const Transform3d &transform) { return transform.linear().col(index); } +inline const Vec3d get_base(unsigned index, const Transform3d &transform) { return transform.linear().col(index); } +inline const Vec3d get_x_base(const Transform3d &transform) { return get_base(0, transform); } +inline const Vec3d get_y_base(const Transform3d &transform) { return get_base(1, transform); } +inline const Vec3d get_z_base(const Transform3d &transform) { return get_base(2, transform); } inline const Vec3d &get_base(unsigned index, const Transform3d::LinearPart &transform) { return transform.col(index); } -inline const Vec3d& get_x_base(const Transform3d &transform) { return get_base(0, transform); } -inline const Vec3d& get_y_base(const Transform3d &transform) { return get_base(1, transform); } -inline const Vec3d& get_z_base(const Transform3d &transform) { return get_base(2, transform); } inline const Vec3d &get_x_base(const Transform3d::LinearPart &transform) { return get_base(0, transform); } inline const Vec3d &get_y_base(const Transform3d::LinearPart &transform) { return get_base(1, transform); } inline const Vec3d &get_z_base(const Transform3d::LinearPart &transform) { return get_base(2, transform); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index 8727391968..3c351a3bb6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -219,6 +219,11 @@ bool GLGizmoSVG::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume->emboss_shape->projection.use_surface) process(); + // TODO: Remove it when it will be stable + // Distance should not change during dragging + const GLVolume *gl_volume = m_parent.get_selection().get_first_volume(); + m_distance = calc_distance(*gl_volume, m_raycast_manager, m_parent); + // Show correct value of height & depth inside of inputs calculate_scale(); } @@ -456,7 +461,8 @@ void GLGizmoSVG::set_volume_by_selection() m_volume_shape = *volume->emboss_shape; // copy // Calculate current angle of up vector - m_angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); + m_angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); + m_distance = calc_distance(*gl_volume, m_raycast_manager, m_parent); // calculate scale for height and depth inside of scaled object instance calculate_scale(); @@ -907,18 +913,12 @@ EmbossShape select_shape() if (shape.svg_file_path.empty()) return {}; - // select units - bool use_inch = wxGetApp().app_config->get_bool("use_inches"); const char *unit_mm{"mm"}; - const char *unit_in{"in"}; - const char *unit = use_inch ?unit_in : unit_mm; - // common used DPI is 96 or 72 float dpi = 96.0f; - NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), unit, dpi); + NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), unit_mm, dpi); ScopeGuard sg([image]() { nsvgDelete(image); }); - shape.scale = 1e-2; // loaded in mm constexpr float tesselation_tolerance = 1e-2f; diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 07c837ca0c..b53976b312 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -8,10 +8,56 @@ #include "slic3r/GUI/I18N.hpp" #include "libslic3r/Emboss.hpp" +using namespace Slic3r; +using namespace Slic3r::GUI; + namespace{ // Distance of embossed volume from surface to be represented as distance surface // Maximal distance is also enlarge by size of emboss depth constexpr Slic3r::MinMax surface_distance_sq{1e-4, 10.}; // [in mm] + +/// +/// Extract position of mouse from mouse event +/// +/// Event +/// Position +Vec2d mouse_position(const wxMouseEvent &mouse_event); + +/// +/// Start dragging +/// +/// +/// +/// +/// +/// +/// +/// True on success start otherwise false +bool start_dragging(const Vec2d &mouse_pos, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + RaycastManager &raycast_manager, + const std::optional &up_limit); + +/// +/// During dragging +/// +/// +/// +/// +/// +/// +/// +/// +bool dragging(const Vec2d &mouse_pos, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + const RaycastManager &raycast_manager, + const std::optional &up_limit); + +std::vector collect_allowed_volumes_id(const ModelVolumePtrs &volumes, const ObjectID &selected_volume_id); } namespace Slic3r::GUI { @@ -99,195 +145,16 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, if (mouse_event.Moving()) return false; - // detect start text dragging - if (mouse_event.LeftDown()) { - // selected volume - GLVolume *gl_volume_ptr = get_selected_gl_volume(canvas); - if (gl_volume_ptr == nullptr) - return false; - const GLVolume &gl_volume = *gl_volume_ptr; - - // is selected volume closest hovered? - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - if (int hovered_idx = canvas.get_first_hover_volume_idx(); - hovered_idx < 0) - return false; - else if (auto hovered_idx_ = static_cast(hovered_idx); - hovered_idx_ >= gl_volumes.size() || - gl_volumes[hovered_idx_] != gl_volume_ptr) - return false; - - const ModelObjectPtrs& objects = canvas.get_model()->objects; - const ModelObject *object = get_model_object(gl_volume, objects); - assert(object != nullptr); - if (object == nullptr) - return false; - - const ModelInstance *instance = get_model_instance(gl_volume, *object); - const ModelVolume *volume = get_model_volume(gl_volume, *object); - assert(instance != nullptr && volume != nullptr); - if (object == nullptr || instance == nullptr || volume == nullptr) - return false; - - // allowed drag&drop by canvas for object - if (volume->is_the_only_one_part()) - return false; - - const ModelVolumePtrs &volumes = object->volumes; - std::vector allowed_volumes_id; - if (volumes.size() > 1) { - allowed_volumes_id.reserve(volumes.size() - 1); - for (const ModelVolume *v : volumes) { - // skip actual selected object - if (v->id() == volume->id()) - continue; - // drag only above part not modifiers or negative surface - if (!v->is_model_part()) - continue; - allowed_volumes_id.emplace_back(v->id().id); - } - } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - RaycastManager::Meshes meshes = create_meshes(canvas, condition); - // initialize raycasters - // INFO: It could slows down for big objects - // (may be move to thread and do not show drag until it finish) - raycast_manager.actualize(*instance, &condition, &meshes); - - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - - // world_matrix_fixed() without sla shift - Transform3d to_world = world_matrix_fixed(gl_volume, objects); - - // zero point of volume in world coordinate system - Vec3d volume_center = to_world.translation(); - // screen coordinate of volume center - Vec2i coor = CameraUtils::project(camera, volume_center); - Vec2d mouse_offset = coor.cast() - mouse_pos; - Vec2d mouse_offset_without_sla_shift = mouse_offset; - if (double sla_shift = gl_volume.get_sla_shift_z(); !is_approx(sla_shift, 0.)) { - Transform3d to_world_without_sla_move = instance->get_matrix() * volume->get_matrix(); - if (volume->emboss_shape.has_value() && volume->emboss_shape->fix_3mf_tr.has_value()) - to_world_without_sla_move = to_world_without_sla_move * (*volume->emboss_shape->fix_3mf_tr); - // zero point of volume in world coordinate system - volume_center = to_world_without_sla_move.translation(); - // screen coordinate of volume center - coor = CameraUtils::project(camera, volume_center); - mouse_offset_without_sla_shift = coor.cast() - mouse_pos; - } - - Transform3d volume_tr = gl_volume.get_volume_transformation().get_matrix(); - - // fix baked transformation from .3mf store process - if (const std::optional &es_opt = volume->emboss_shape; - es_opt.has_value()) { - const std::optional &fix = es_opt->fix_3mf_tr; - if (fix.has_value()) - volume_tr = volume_tr * fix->inverse(); - } - - Transform3d instance_tr = instance->get_matrix(); - Transform3d instance_tr_inv = instance_tr.inverse(); - Transform3d world_tr = instance_tr * volume_tr; - std::optional start_angle; - if (up_limit.has_value()) - start_angle = Emboss::calc_up(world_tr, *up_limit); - - std::optional start_distance; - if (!volume->emboss_shape->projection.use_surface) - start_distance = calc_distance(gl_volume, raycast_manager, &condition); - surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume_ptr, condition, start_angle, start_distance, true, mouse_offset_without_sla_shift }; - - // disable moving with object by mouse - canvas.enable_moving(false); - canvas.enable_picking(false); - return true; - } + if (mouse_event.LeftDown()) + return start_dragging(mouse_position(mouse_event), camera, surface_drag, canvas, raycast_manager, up_limit); // Dragging starts out of window if (!surface_drag.has_value()) return false; - if (mouse_event.Dragging()) { - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset_without_sla_shift; + if (mouse_event.Dragging()) + return dragging(mouse_position(mouse_event), camera, surface_drag, canvas, raycast_manager, up_limit); - std::optional hit = ray_from_camera( - raycast_manager, offseted_mouse, camera, &surface_drag->condition); - - surface_drag->exist_hit = hit.has_value(); - if (!hit.has_value()) { - // cross hair need redraw - canvas.set_as_dirty(); - return true; - } - - auto world_linear = surface_drag->world.linear(); - // Calculate offset: transformation to wanted position - { - // Reset skew of the text Z axis: - // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. - Vec3d old_z = world_linear.col(2); - Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); - world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); - } - - Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() - auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); - Transform3d world_new = z_rotation * surface_drag->world; - auto world_new_linear = world_new.linear(); - - // Fix direction of up vector to zero initial rotation - if(up_limit.has_value()){ - Vec3d z_world = world_new_linear.col(2); - z_world.normalize(); - Vec3d wanted_up = Emboss::suggest_up(z_world, *up_limit); - - Vec3d y_world = world_new_linear.col(1); - auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); - - world_new = y_rotation * world_new; - world_new_linear = world_new.linear(); - } - - // Edit position from right - Transform3d volume_new{Eigen::Translation(surface_drag->instance_inv * hit->position)}; - volume_new.linear() = surface_drag->instance_inv.linear() * world_new_linear; - - // Check that transformation matrix is valid transformation - assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN - if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) - return true; - - // Check that scale in world did not changed - assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); - assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); - - const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects); - // fix baked transformation from .3mf store process - if (volume != nullptr && volume->emboss_shape.has_value()) { - const std::optional &fix = volume->emboss_shape->fix_3mf_tr; - if (fix.has_value()) - volume_new = volume_new * (*fix); - - // apply move in Z direction and rotation by up vector - Emboss::apply_transformation(surface_drag->start_angle, surface_drag->start_distance, volume_new); - } - - // Update transformation for all instances - for (GLVolume *vol : canvas.get_volumes().volumes) { - if (vol->object_idx() != surface_drag->gl_volume->object_idx() || vol->volume_idx() != surface_drag->gl_volume->volume_idx()) - continue; - vol->set_volume_transformation(volume_new); - } - - canvas.set_as_dirty(); - return true; - } return false; } @@ -311,11 +178,10 @@ std::optional calc_surface_offset(const Selection &selection, RaycastMana raycast_manager.actualize(*instance, &cond); Transform3d to_world = world_matrix_fixed(gl_volume, objects); - Vec3d point = to_world * Vec3d::Zero(); - Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); - + Vec3d point = to_world.translation(); + Vec3d dir = -get_z_base(to_world); // ray in direction of text projection(from volume zero to z-dir) - std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); + std::optional hit_opt = raycast_manager.closest_hit(point, dir, &cond); // Try to find closest point when no hit object in emboss direction if (!hit_opt.has_value()) { @@ -366,19 +232,7 @@ std::optional calc_distance(const GLVolume &gl_volume, RaycastManager &ra if (volume->is_the_only_one_part()) return {}; - const ModelVolumePtrs &volumes = object->volumes; - std::vector allowed_volumes_id; - allowed_volumes_id.reserve(volumes.size() - 1); - for (const ModelVolume *v : volumes) { - // skip actual selected object - if (v->id() == volume->id()) - continue; - // collect hit only from object parts not modifiers neither negative - if (!v->is_model_part()) - continue; - allowed_volumes_id.emplace_back(v->id().id); - } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + RaycastManager::AllowVolumes condition = create_condition(object->volumes, volume->id()); RaycastManager::Meshes meshes = create_meshes(canvas, condition); raycaster.actualize(*instance, &condition, &meshes); return calc_distance(gl_volume, raycaster, &condition); @@ -388,27 +242,34 @@ std::optional calc_distance(const GLVolume &gl_volume, const RaycastManag { Transform3d w = gl_volume.world_matrix(); Vec3d p = w.translation(); - const Vec3d& dir = get_z_base(w); + Vec3d dir = -get_z_base(w); auto hit_opt = raycaster.closest_hit(p, dir, condition); if (!hit_opt.has_value()) return {}; + const RaycastManager::Hit &hit = *hit_opt; + // NOTE: hit.squared_distance is in volume space not world + + const Transform3d &tr = raycaster.get_transformation(hit.tr_key); + Vec3d hit_world = tr * hit.position; + Vec3d p_to_hit = hit_world - p; + double distance_sq = p_to_hit.squaredNorm(); // too small distance is calculated as zero distance - if (hit.squared_distance < ::surface_distance_sq.min) + if (distance_sq < ::surface_distance_sq.min) return {}; // check maximal distance const BoundingBoxf3& bb = gl_volume.bounding_box(); double max_squared_distance = std::max(std::pow(2 * bb.size().z(), 2), ::surface_distance_sq.max); - if (hit.squared_distance > max_squared_distance) - return {}; - + if (distance_sq > max_squared_distance) + return {}; + // calculate sign - float sign = ((hit.position - p).dot(dir) > 0)? 1.f : -1.f; + float sign = (p_to_hit.dot(dir) > 0)? 1.f : -1.f; // distiguish sign - return sign * static_cast(sqrt(hit.squared_distance)); + return sign * static_cast(sqrt(distance_sq)); } Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects) @@ -523,4 +384,189 @@ void do_local_z_move(GLCanvas3D &canvas, double relative_move) { canvas.do_move(snapshot_name); } -} // namespace Slic3r::GUI \ No newline at end of file +} // namespace Slic3r::GUI + +// private implementation +namespace { + +Vec2d mouse_position(const wxMouseEvent &mouse_event){ + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + return mouse_coord.cast(); +} + +bool start_dragging(const Vec2d &mouse_pos, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + RaycastManager &raycast_manager, + const std::optional&up_limit) +{ + // selected volume + GLVolume *gl_volume_ptr = get_selected_gl_volume(canvas); + if (gl_volume_ptr == nullptr) + return false; + const GLVolume &gl_volume = *gl_volume_ptr; + + // is selected volume closest hovered? + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + if (int hovered_idx = canvas.get_first_hover_volume_idx(); hovered_idx < 0) + return false; + else if (auto hovered_idx_ = static_cast(hovered_idx); + hovered_idx_ >= gl_volumes.size() || gl_volumes[hovered_idx_] != gl_volume_ptr) + return false; + + const ModelObjectPtrs &objects = canvas.get_model()->objects; + const ModelObject *object = get_model_object(gl_volume, objects); + assert(object != nullptr); + if (object == nullptr) + return false; + + const ModelInstance *instance = get_model_instance(gl_volume, *object); + const ModelVolume *volume = get_model_volume(gl_volume, *object); + assert(instance != nullptr && volume != nullptr); + if (object == nullptr || instance == nullptr || volume == nullptr) + return false; + + // allowed drag&drop by canvas for object + if (volume->is_the_only_one_part()) + return false; + + RaycastManager::AllowVolumes condition = create_condition(object->volumes, volume->id()); + RaycastManager::Meshes meshes = create_meshes(canvas, condition); + // initialize raycasters + // INFO: It could slows down for big objects + // (may be move to thread and do not show drag until it finish) + raycast_manager.actualize(*instance, &condition, &meshes); + + // world_matrix_fixed() without sla shift + Transform3d to_world = world_matrix_fixed(gl_volume, objects); + + // zero point of volume in world coordinate system + Vec3d volume_center = to_world.translation(); + // screen coordinate of volume center + Vec2i coor = CameraUtils::project(camera, volume_center); + Vec2d mouse_offset = coor.cast() - mouse_pos; + Vec2d mouse_offset_without_sla_shift = mouse_offset; + if (double sla_shift = gl_volume.get_sla_shift_z(); !is_approx(sla_shift, 0.)) { + Transform3d to_world_without_sla_move = instance->get_matrix() * volume->get_matrix(); + if (volume->emboss_shape.has_value() && volume->emboss_shape->fix_3mf_tr.has_value()) + to_world_without_sla_move = to_world_without_sla_move * (*volume->emboss_shape->fix_3mf_tr); + // zero point of volume in world coordinate system + volume_center = to_world_without_sla_move.translation(); + // screen coordinate of volume center + coor = CameraUtils::project(camera, volume_center); + mouse_offset_without_sla_shift = coor.cast() - mouse_pos; + } + + Transform3d volume_tr = gl_volume.get_volume_transformation().get_matrix(); + + // fix baked transformation from .3mf store process + if (const std::optional &es_opt = volume->emboss_shape; es_opt.has_value()) { + const std::optional &fix = es_opt->fix_3mf_tr; + if (fix.has_value()) + volume_tr = volume_tr * fix->inverse(); + } + + Transform3d instance_tr = instance->get_matrix(); + Transform3d instance_tr_inv = instance_tr.inverse(); + Transform3d world_tr = instance_tr * volume_tr; + std::optional start_angle; + if (up_limit.has_value()) + start_angle = Emboss::calc_up(world_tr, *up_limit); + + std::optional start_distance; + if (!volume->emboss_shape->projection.use_surface) + start_distance = calc_distance(gl_volume, raycast_manager, &condition); + surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, + gl_volume_ptr, condition, start_angle, + start_distance, true, mouse_offset_without_sla_shift}; + + // disable moving with object by mouse + canvas.enable_moving(false); + canvas.enable_picking(false); + return true; +} + +bool dragging(const Vec2d &mouse_pos, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + const RaycastManager &raycast_manager, + const std::optional &up_limit) +{ + Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset_without_sla_shift; + std::optional hit = ray_from_camera( + raycast_manager, offseted_mouse, camera, &surface_drag->condition); + + surface_drag->exist_hit = hit.has_value(); + if (!hit.has_value()) { + // cross hair need redraw + canvas.set_as_dirty(); + return true; + } + + auto world_linear = surface_drag->world.linear(); + // Calculate offset: transformation to wanted position + { + // Reset skew of the text Z axis: + // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. + Vec3d old_z = world_linear.col(2); + Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); + world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + } + + Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() + auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); + Transform3d world_new = z_rotation * surface_drag->world; + auto world_new_linear = world_new.linear(); + + // Fix direction of up vector to zero initial rotation + if(up_limit.has_value()){ + Vec3d z_world = world_new_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = Emboss::suggest_up(z_world, *up_limit); + + Vec3d y_world = world_new_linear.col(1); + auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + + world_new = y_rotation * world_new; + world_new_linear = world_new.linear(); + } + + // Edit position from right + Transform3d volume_new{Eigen::Translation(surface_drag->instance_inv * hit->position)}; + volume_new.linear() = surface_drag->instance_inv.linear() * world_new_linear; + + // Check that transformation matrix is valid transformation + assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) + return true; + + // Check that scale in world did not changed + assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); + assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); + + const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects); + // fix baked transformation from .3mf store process + if (volume != nullptr && volume->emboss_shape.has_value()) { + const std::optional &fix = volume->emboss_shape->fix_3mf_tr; + if (fix.has_value()) + volume_new = volume_new * (*fix); + + // apply move in Z direction and rotation by up vector + Emboss::apply_transformation(surface_drag->start_angle, surface_drag->start_distance, volume_new); + } + + // Update transformation for all instances + for (GLVolume *vol : canvas.get_volumes().volumes) { + if (vol->object_idx() != surface_drag->gl_volume->object_idx() || vol->volume_idx() != surface_drag->gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(volume_new); + } + + canvas.set_as_dirty(); + return true; +} + +} // namespace diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index b2b7588555..ee7c4c0bdb 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -182,7 +182,8 @@ std::optional RaycastManager::closest_hit(const Vec3d &poin std::vector hits_neg = mesh->query_ray_hits(point_negative, -mesh_direction); hits.insert(hits.end(), std::make_move_iterator(hits_neg.begin()), std::make_move_iterator(hits_neg.end())); for (const AABBMesh::hit_result &hit : hits) { - double squared_distance = (mesh_point - hit.position()).squaredNorm(); + Vec3d diff = mesh_point - hit.position(); + double squared_distance = diff.squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; @@ -359,4 +360,23 @@ std::optional ray_from_camera(const RaycastManager & return raycaster.first_hit(point, direction, skip); } +RaycastManager::AllowVolumes create_condition(const ModelVolumePtrs &volumes, const ObjectID &disallowed_volume_id) { + std::vector allowed_volumes_id; + if (volumes.size() > 1) { + allowed_volumes_id.reserve(volumes.size() - 1); + for (const ModelVolume *v : volumes) { + // drag only above part not modifiers or negative surface + if (!v->is_model_part()) + continue; + + // skip actual selected object + if (v->id() == disallowed_volume_id) + continue; + + allowed_volumes_id.emplace_back(v->id().id); + } + } + return RaycastManager::AllowVolumes(allowed_volumes_id); +} + } // namespace Slic3r::GUI diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 41ec82d6c8..4e143ba5dd 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -167,6 +167,14 @@ std::optional ray_from_camera(const RaycastManager & const Camera &camera, const RaycastManager::ISkip *skip); +/// +/// Create condition to allowe only parts from volumes without one given +/// +/// List of allowed volumes included one which is dissalowed and non parts +/// Disallowed volume +/// Condition +RaycastManager::AllowVolumes create_condition(const ModelVolumePtrs &volumes, const ObjectID &disallowed_volume_id); + } // namespace Slic3r::GUI #endif // slic3r_RaycastManager_hpp_