diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index e3f7454a76..44fa402025 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1540,8 +1540,28 @@ std::optional Emboss::ProjectZ::unproject(const Vec3d &p, double *depth) return Vec2d(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE); } -Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, - const Vec3f &normal, + +Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) +{ + // Normal must be 1 + assert(is_approx(normal.norm(), 1.)); + + // wanted up direction of result + Vec3d wanted_up_side = + (std::fabs(normal.z()) > up_limit)? + Vec3d::UnitY() : Vec3d::UnitZ(); + + // create perpendicular unit vector to surface triangle normal vector + // lay on surface of triangle and define up vector for text + Vec3d wanted_up_dir = normal.cross(wanted_up_side).cross(normal); + // normal3d is NOT perpendicular to normal_up_dir + wanted_up_dir.normalize(); + + return wanted_up_dir; +} + +Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, + const Vec3d &normal, float up_limit) { // up and emboss direction for generated model @@ -1552,28 +1572,27 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Vec3d wanted_up_side = Vec3d::UnitZ(); if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY(); - Vec3d wanted_emboss_dir = normal.cast(); // after cast from float it needs to be normalized again - wanted_emboss_dir.normalize(); + assert(is_approx(normal.norm(), 1.)); // create perpendicular unit vector to surface triangle normal vector // lay on surface of triangle and define up vector for text - Vec3d wanted_up_dir = wanted_emboss_dir + Vec3d wanted_up_dir = normal .cross(wanted_up_side) - .cross(wanted_emboss_dir); + .cross(normal); // normal3d is NOT perpendicular to normal_up_dir wanted_up_dir.normalize(); // perpendicular to emboss vector of text and normal Vec3d axis_view; double angle_view; - if (wanted_emboss_dir == -Vec3d::UnitZ()) { + if (normal == -Vec3d::UnitZ()) { // text_emboss_dir has opposit direction to wanted_emboss_dir axis_view = Vec3d::UnitY(); angle_view = M_PI; } else { - axis_view = text_emboss_dir.cross(wanted_emboss_dir); - angle_view = std::acos(text_emboss_dir.dot(wanted_emboss_dir)); // in rad + axis_view = text_emboss_dir.cross(normal); + angle_view = std::acos(text_emboss_dir.dot(normal)); // in rad axis_view.normalize(); } @@ -1593,7 +1612,7 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Eigen::AngleAxis up_rot(angle_up, text_emboss_dir); Transform3d transform = Transform3d::Identity(); - transform.translate(position.cast()); + transform.translate(position); transform.rotate(view_rot); transform.rotate(up_rot); return transform; diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index ca27afe45c..cf15aa2cb9 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -273,7 +273,15 @@ namespace Emboss /// Define transformation from 2d to 3d(orientation, position, scale, ...) /// Projected shape into space indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection); - + + /// + /// Suggest wanted up vector of embossed text by emboss direction + /// + /// Normalized vector of emboss direction in world + /// Is compared with normal.z to suggest up direction + /// Wanted up vector + Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9); + /// /// Create transformation for emboss text object to lay on surface point /// @@ -282,7 +290,7 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Transformation onto surface point Transform3d create_transformation_onto_surface( - const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f); + const Vec3d &position, const Vec3d &normal, float up_limit = 0.9f); class ProjectZ : public IProjection { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index c6ce9da8fe..4493fba38e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -828,42 +828,38 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + auto hit = m_raycast_manager.ray_from_camera(offseted_mouse, camera, &m_surface_drag->condition); m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw m_parent.set_as_dirty(); return true; } - // Calculate offset: transformation to wanted position - Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; - Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); - - Vec3d hit_position = hit->position.cast(); - Vec3d offset_volume = hit_to_volume * hit_position; - Transform3d translate{Eigen::Translation(offset_volume)}; - - Transform3d rotate; - // normal transformed to volume - Vec3d hit_normal = hit->normal.cast(); - Vec3d z_t = hit_to_volume.linear() * hit_normal; - bool exist_rotate = priv::allign_z(z_t, rotate); - // Edit position from right - Transform3d volume_new = m_surface_drag->volume_tr * translate * rotate; const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); const Transform3d &volume = m_surface_drag->volume_tr; - Vec3d hit_position_world = hit_to_world * hit_position; - Vec3d hit_normal_world = hit_to_world.linear() * hit_normal; + // Calculate offset: transformation to wanted position + Transform3d text_to_world_old = instance * volume; + { + // 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 = text_to_world_old.linear().col(2); + Vec3d new_z = text_to_world_old.linear().col(0).cross(text_to_world_old.linear().col(1)); + text_to_world_old.linear().col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + } - // REWRITE transformation - Transform3d volume_R = priv::surface_transformR(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); - volume_new = volume_R; + // normal transformed to volume + Vec3d text_z_world = text_to_world_old.linear() * Vec3d::UnitZ(); + auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); + Transform3d text_to_world_new = z_rotation * text_to_world_old; + + // Fix up vector ?? + //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + + // Edit position from right + Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; + volume_new.linear() = instance.linear().inverse() * text_to_world_new.linear(); 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)) @@ -871,14 +867,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Check scale in world // Calculate Scale to keep size after move over scaled surface - Transform3d current_world = instance * volume; - auto current_world_linear = current_world.linear(); - - Transform3d wanted_world = instance * volume_new; - auto wanted_world_linear = wanted_world.linear(); - - m_surface_drag->y_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitY()); - m_surface_drag->z_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitZ()); + m_surface_drag->y_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitY()); + m_surface_drag->z_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitZ()); // recalculate rotation for scaled volume //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); @@ -4245,7 +4235,7 @@ bool priv::start_create_volume_on_surface_job( raycaster.actualize(obj, &cond); const Camera &camera = plater->get_camera(); - std::optional hit = raycaster.unproject(screen_coor, camera); + std::optional hit = raycaster.ray_from_camera(screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set @@ -4253,15 +4243,14 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - Transform3d hit_to_world = raycaster.get_transformation(hit->tr_key); // priv::reset_skew(hit_to_world); Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position.cast(), hit->normal.cast()); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d world_new = hit_to_world * surface_trmat; + Transform3d world_new = surface_trmat; // Reset skew priv::reset_skew_respect_z(world_new); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index fad41424f9..3f34c37fcd 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -19,14 +19,6 @@ static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager:: return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; - - // Copy functionality from MeshRaycaster::unproject_on_mesh without filtering -using SurfacePoint = RaycastManager::SurfacePoint; -static std::optional unproject_on_mesh(const AABBMesh &aabb_mesh, - const Vec2d &mouse_pos, - const Transform3d &transformation, - const Camera &camera); - } void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) @@ -108,11 +100,21 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) if (need_sort) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } - -std::optional RaycastManager::unproject( + +std::optional RaycastManager::ray_from_camera( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { - std::optional closest; + // Improve it is not neccessaru to use AABBMesh and calc normal in + + struct Result + { + const AABBMesh *mesh = nullptr; + double squared_distance; + int face; + Vec3d hit_world; + const Transform3d *tramsformation; + const TrKey *key; + }result; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -120,18 +122,46 @@ std::optional RaycastManager::unproject( const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - auto surface_point_opt = - priv::unproject_on_mesh(*mesh, mouse_pos, transformation, camera); - if (!surface_point_opt.has_value()) + + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv * point; + direction = inv.linear() * direction; + std::vector hits = mesh->query_ray_hits(point, direction); + if (hits.empty()) continue; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + + // convert to world + Vec3d hit_world = transformation * hit.position(); + double squared_distance = (camera.get_position() - hit_world).squaredNorm(); + if (result.mesh != nullptr && + result.squared_distance < squared_distance) continue; - Vec3d act_hit_tr = transformation * surface_point_opt->position.cast(); - double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); - if (closest.has_value() && - closest->squared_distance < squared_distance) - continue; - closest = Hit{*surface_point_opt, key, squared_distance}; + + result.mesh = mesh; + result.squared_distance = squared_distance; + result.face = hit.face(); + result.hit_world = hit_world; + result.tramsformation = &transformation; + result.key = &key; } - return closest; + + if (result.mesh == nullptr) + return {}; + + const Vec3i tri = result.mesh->indices(result.face); + Vec3d pts[3]; + auto tr = result.tramsformation->linear(); + for (int i = 0; i < 3; ++i) + pts[i] = tr * result.mesh->vertices(tri[i]).cast(); + Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); + normal_world.normalize(); + + SurfacePoint point_world{result.hit_world, normal_world}; + return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; } std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const @@ -241,26 +271,6 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu } } -std::optional priv::unproject_on_mesh(const AABBMesh &aabb_mesh, - const Vec2d &mouse_pos, - const Transform3d &transformation, - const Camera &camera) -{ - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - Transform3d inv = transformation.inverse(); - point = inv * point; - direction = inv.linear() * direction; - std::vector hits = aabb_mesh.query_ray_hits(point, direction); - - if (hits.empty()) - return {}; // no intersection found - - const AABBMesh::hit_result &hit = hits.front(); - return priv::SurfacePoint{hit.position(), hit.normal()}; -} - const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) { auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 406e51c86c..5bd28d00a6 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -114,10 +114,9 @@ public: /// Position of mouse on screen /// Projection params /// Define which caster will be skipped, null mean no skip - /// Position on surface, normal direction and transformation key, which define hitted object instance - std::optional unproject(const Vec2d &mouse_pos, - const Camera &camera, - const ISkip *skip = nullptr) const; + /// Position on surface, normal direction in world coorinate + /// + key, to know hitted instance and volume + std::optional ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const; /// /// Unproject Ray(point direction) on mesh by MeshRaycasters