mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 10:06:02 +08:00
Move over surface with relative transformation
This commit is contained in:
parent
67155e8da0
commit
b82f1fe818
@ -1540,8 +1540,28 @@ std::optional<Vec2d> 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<double>();
|
||||
// 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<double>());
|
||||
transform.translate(position);
|
||||
transform.rotate(view_rot);
|
||||
transform.rotate(up_rot);
|
||||
return transform;
|
||||
|
@ -274,6 +274,14 @@ namespace Emboss
|
||||
/// <returns>Projected shape into space</returns>
|
||||
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
|
||||
|
||||
/// <summary>
|
||||
/// Suggest wanted up vector of embossed text by emboss direction
|
||||
/// </summary>
|
||||
/// <param name="normal">Normalized vector of emboss direction in world</param>
|
||||
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
||||
/// <returns>Wanted up vector</returns>
|
||||
Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9);
|
||||
|
||||
/// <summary>
|
||||
/// Create transformation for emboss text object to lay on surface point
|
||||
/// </summary>
|
||||
@ -282,7 +290,7 @@ namespace Emboss
|
||||
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
||||
/// <returns>Transformation onto surface point</returns>
|
||||
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
|
||||
{
|
||||
|
@ -828,42 +828,38 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
||||
Vec2d mouse_pos = mouse_coord.cast<double>();
|
||||
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<double>();
|
||||
Vec3d offset_volume = hit_to_volume * hit_position;
|
||||
Transform3d translate{Eigen::Translation<double, 3>(offset_volume)};
|
||||
|
||||
Transform3d rotate;
|
||||
// normal transformed to volume
|
||||
Vec3d hit_normal = hit->normal.cast<double>();
|
||||
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<double, Eigen::DontAlign>::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<double, Eigen::DontAlign>::FromTwoVectors(text_y_world, hit->normal);
|
||||
|
||||
// Edit position from right
|
||||
Transform3d volume_new{Eigen::Translation<double, 3>(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<RaycastManager::Hit> hit = raycaster.unproject(screen_coor, camera);
|
||||
std::optional<RaycastManager::Hit> 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<float>(), hit->normal.cast<float>());
|
||||
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);
|
||||
|
@ -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<double>;
|
||||
static std::optional<SurfacePoint> 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)
|
||||
@ -109,10 +101,20 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip)
|
||||
std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower);
|
||||
}
|
||||
|
||||
std::optional<RaycastManager::Hit> RaycastManager::unproject(
|
||||
std::optional<RaycastManager::Hit> RaycastManager::ray_from_camera(
|
||||
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
|
||||
{
|
||||
std::optional<Hit> 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::Hit> 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<AABBMesh::hit_result> 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>();
|
||||
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<double>();
|
||||
Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]);
|
||||
normal_world.normalize();
|
||||
|
||||
SurfacePoint<double> point_world{result.hit_world, normal_world};
|
||||
return RaycastManager::Hit{point_world, *result.key, result.squared_distance};
|
||||
}
|
||||
|
||||
std::optional<RaycastManager::Hit> 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::SurfacePoint> 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<AABBMesh::hit_result> 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; };
|
||||
|
@ -114,10 +114,9 @@ public:
|
||||
/// <param name="mouse_pos">Position of mouse on screen</param>
|
||||
/// <param name="camera">Projection params</param>
|
||||
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
|
||||
/// <returns>Position on surface, normal direction and transformation key, which define hitted object instance</returns>
|
||||
std::optional<Hit> unproject(const Vec2d &mouse_pos,
|
||||
const Camera &camera,
|
||||
const ISkip *skip = nullptr) const;
|
||||
/// <returns>Position on surface, normal direction in world coorinate
|
||||
/// + key, to know hitted instance and volume</returns>
|
||||
std::optional<Hit> ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const;
|
||||
|
||||
/// <summary>
|
||||
/// Unproject Ray(point direction) on mesh by MeshRaycasters
|
||||
|
Loading…
x
Reference in New Issue
Block a user