extend functionality of surface drag

This commit is contained in:
Filip Sykala - NTB T15p 2023-02-28 16:17:09 +01:00
parent a1a57eb61c
commit b7f4159d57
5 changed files with 149 additions and 118 deletions

View File

@ -345,16 +345,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
}
namespace priv {
/// <summary>
/// Get transformation to world
/// - use fix after store to 3mf when exists
/// </summary>
/// <param name="gl_volume">Scene volume</param>
/// <param name="model">To identify MovelVolume with fix transformation</param>
/// <returns></returns>
static Transform3d world_matrix(const GLVolume *gl_volume, const Model *model);
static Transform3d world_matrix(const Selection &selection);
/// <summary>
/// Change position of emboss window
/// </summary>
@ -363,36 +353,6 @@ static Transform3d world_matrix(const Selection &selection);
static void change_window_position(std::optional<ImVec2> &output_window_offset, bool try_to_fix);
} // namespace priv
Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model)
{
if (!gl_volume)
return Transform3d::Identity();
Transform3d res = gl_volume->world_matrix();
if (!model)
return res;
const ModelVolume* mv = get_model_volume(*gl_volume, model->objects);
if (!mv)
return res;
const std::optional<TextConfiguration> &tc = mv->text_configuration;
if (!tc.has_value())
return res;
const std::optional<Transform3d> &fix = tc->fix_3mf_tr;
if (!fix.has_value())
return res;
return res * fix->inverse();
}
Transform3d priv::world_matrix(const Selection &selection)
{
const GLVolume *gl_volume = get_selected_gl_volume(selection);
return world_matrix(gl_volume, selection.get_model());
}
bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
{
// exist selected volume?
@ -1115,17 +1075,6 @@ static inline void execute_job(std::shared_ptr<Job> j)
});
}
namespace priv {
/// <summary>
/// Calculate translation of text volume onto surface of model
/// </summary>
/// <param name="volume">Text</param>
/// <param name="raycast_manager">AABB trees of object. Actualize object containing text</param>
/// <param name="selection">Transformation of actual instance</param>
/// <returns>Offset of volume in volume coordinate</returns>
std::optional<Vec3d> calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection);
} // namespace priv
bool GLGizmoEmboss::process()
{
// no volume is selected -> selection from right panel
@ -1160,7 +1109,7 @@ bool GLGizmoEmboss::process()
// when it is new applying of use surface than move origin onto surfaca
if (!m_volume->text_configuration->style.prop.use_surface) {
auto offset = priv::calc_surface_offset(*m_volume, m_raycast_manager, m_parent.get_selection());
auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager);
if (offset.has_value())
text_tr *= Eigen::Translation<double, 3>(*offset);
}
@ -1231,7 +1180,7 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) {
if (sel.is_empty()) return false;
// camera direction transformed into volume coordinate system
Transform3d to_world = priv::world_matrix(sel);
Transform3d to_world = world_matrix_fixed(sel);
Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir;
cam_dir_tr.normalize();
@ -2949,53 +2898,6 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle)
m_parent.do_rotate(snapshot_name);
}
std::optional<Vec3d> priv::calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection) {
// Move object on surface
auto cond = RaycastManager::SkipVolume(volume.id().id);
raycast_manager.actualize(volume.get_object(), &cond);
//const Selection &selection = m_parent.get_selection();
const GLVolume *gl_volume = get_selected_gl_volume(selection);
Transform3d to_world = priv::world_matrix(gl_volume, selection.get_model());
Vec3d point = to_world * Vec3d::Zero();
Vec3d direction = to_world.linear() * (-Vec3d::UnitZ());
// ray in direction of text projection(from volume zero to z-dir)
std::optional<RaycastManager::Hit> hit_opt = raycast_manager.closest_hit(point, direction, &cond);
// Try to find closest point when no hit object in emboss direction
if (!hit_opt.has_value()) {
std::optional<RaycastManager::ClosePoint> close_point_opt = raycast_manager.closest(point);
// It should NOT appear. Closest point always exists.
assert(close_point_opt.has_value());
if (!close_point_opt.has_value())
return {};
// It is no neccesary to move with origin by very small value
if (close_point_opt->squared_distance < EPSILON)
return {};
const RaycastManager::ClosePoint &close_point = *close_point_opt;
Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key);
Vec3d hit_world = hit_tr * close_point.point;
Vec3d offset_world = hit_world - point; // vector in world
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
return offset_volume;
}
// It is no neccesary to move with origin by very small value
const RaycastManager::Hit &hit = *hit_opt;
if (hit.squared_distance < EPSILON)
return {};
Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key);
Vec3d hit_world = hit_tr * hit.position;
Vec3d offset_world = hit_world - point; // vector in world
// TIP: It should be close to only z move
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
return offset_volume;
}
void GLGizmoEmboss::draw_advanced()
{
const auto &ff = m_style_manager.get_font_file_with_cache();
@ -3610,7 +3512,7 @@ bool priv::start_create_volume_on_surface_job(
auto cond = RaycastManager::AllowVolumes({vol_id});
RaycastManager::Meshes meshes = create_meshes(canvas, cond);
raycaster.actualize(obj, &cond, &meshes);
raycaster.actualize(*obj, &cond, &meshes);
const Camera &camera = plater->get_camera();
std::optional<RaycastManager::Hit> hit = ray_from_camera(raycaster, screen_coor, camera, &cond);

View File

@ -105,10 +105,13 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
return false;
const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->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(object != nullptr && instance != nullptr && volume != nullptr);
assert(instance != nullptr && volume != nullptr);
if (object == nullptr || instance == nullptr || volume == nullptr)
return false;
@ -131,7 +134,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// 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);
raycast_manager.actualize(*instance, &condition, &meshes);
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
@ -242,4 +245,92 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
return false;
}
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager) {
const GLVolume *gl_volume_ptr = get_selected_gl_volume(selection);
if (gl_volume_ptr == nullptr)
return {};
const GLVolume& gl_volume = *gl_volume_ptr;
const ModelObjectPtrs &objects = selection.get_model()->objects;
const ModelVolume* volume = get_model_volume(gl_volume, objects);
if (volume == nullptr)
return {};
const ModelInstance* instance = get_model_instance(gl_volume, objects);
if (instance == nullptr)
return {};
// Move object on surface
auto cond = RaycastManager::SkipVolume(volume->id().id);
raycast_manager.actualize(*instance, &cond);
Transform3d to_world = world_matrix_fixed(gl_volume, selection.get_model()->objects);
Vec3d point = to_world * Vec3d::Zero();
Vec3d direction = to_world.linear() * (-Vec3d::UnitZ());
// ray in direction of text projection(from volume zero to z-dir)
std::optional<RaycastManager::Hit> hit_opt = raycast_manager.closest_hit(point, direction, &cond);
// Try to find closest point when no hit object in emboss direction
if (!hit_opt.has_value()) {
std::optional<RaycastManager::ClosePoint> close_point_opt = raycast_manager.closest(point);
// It should NOT appear. Closest point always exists.
assert(close_point_opt.has_value());
if (!close_point_opt.has_value())
return {};
// It is no neccesary to move with origin by very small value
if (close_point_opt->squared_distance < EPSILON)
return {};
const RaycastManager::ClosePoint &close_point = *close_point_opt;
Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key);
Vec3d hit_world = hit_tr * close_point.point;
Vec3d offset_world = hit_world - point; // vector in world
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
return offset_volume;
}
// It is no neccesary to move with origin by very small value
const RaycastManager::Hit &hit = *hit_opt;
if (hit.squared_distance < EPSILON)
return {};
Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key);
Vec3d hit_world = hit_tr * hit.position;
Vec3d offset_world = hit_world - point; // vector in world
// TIP: It should be close to only z move
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
return offset_volume;
}
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects)
{
Transform3d res = gl_volume.world_matrix();
const ModelVolume *mv = get_model_volume(gl_volume, objects);
if (!mv)
return res;
const std::optional<TextConfiguration> &tc = mv->text_configuration;
if (!tc.has_value())
return res;
const std::optional<Transform3d> &fix = tc->fix_3mf_tr;
if (!fix.has_value())
return res;
return res * fix->inverse();
}
Transform3d world_matrix_fixed(const Selection &selection)
{
const GLVolume *gl_volume = get_selected_gl_volume(selection);
assert(gl_volume != nullptr);
if (gl_volume == nullptr)
return Transform3d::Identity();
return world_matrix_fixed(*gl_volume, selection.get_model()->objects);
}
} // namespace Slic3r::GUI

View File

@ -12,6 +12,7 @@ class GLVolume;
namespace Slic3r::GUI {
class GLCanvas3D;
class Selection;
struct Camera;
// Data for drag&drop over surface with mouse
@ -37,11 +38,48 @@ struct SurfaceDrag
bool exist_hit = true;
};
/// <summary>
/// Mouse event handler, when move(drag&drop) volume over model surface
/// NOTE: Dragged volume has to be selected. And also has to be hovered on start of dragging.
/// </summary>
/// <param name="mouse_event">Contain type of event and mouse position</param>
/// <param name="camera">Actual viewport of camera</param>
/// <param name="surface_drag">Structure which keep information about dragging</param>
/// <param name="canvas">Contain gl_volumes and selection</param>
/// <param name="raycast_manager">AABB trees for raycast in object
/// Refresh state inside of function </param>
/// <returns>True when event is processed otherwise false</returns>
bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
const Camera &camera,
std::optional<SurfaceDrag> &surface_drag,
GLCanvas3D &canvas,
RaycastManager &raycast_manager);
/// <summary>
/// Calculate translation of volume onto surface of model
/// </summary>
/// <param name="selection">Must contain only one selected volume, Transformation of current instance</param>
/// <param name="raycast_manager">AABB trees of object. Actualize object</param>
/// <returns>Offset of volume in volume coordinate</returns>
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager);
/// <summary>
/// Get transformation to world
/// - use fix after store to 3mf when exists
/// </summary>
/// <param name="gl_volume">Scene volume</param>
/// <param name="objects">To identify Model volume with fix transformation</param>
/// <returns>Fixed Transformation of gl_volume</returns>
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs& objects);
/// <summary>
/// Get transformation to world
/// - use fix after store to 3mf when exists
/// NOTE: when not one volume selected return identity
/// </summary>
/// <param name="selection">Selected volume</param>
/// <returns>Fixed Transformation of selected volume in selection</returns>
Transform3d world_matrix_fixed(const Selection &selection);
} // namespace Slic3r::GUI
#endif // slic3r_SurfaceDrag_hpp_

View File

@ -7,8 +7,8 @@ namespace priv {
using namespace Slic3r;
static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr);
static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id);
static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){
return std::make_pair(instance->id().id, volume->id().id); }
static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){
return std::make_pair(instance.id().id, volume.id().id); }
static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key);
static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) {
return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; }
@ -16,23 +16,23 @@ static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrI
return is_lower_key(i1.first, i2.first); };
}
void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Meshes *meshes)
void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes)
{
// actualize MeshRaycaster
priv::actualize(m_meshes, object->volumes, skip, meshes);
priv::actualize(m_meshes, object.volumes, skip, meshes);
// check if inscance was removed
std::vector<bool> removed_transf(m_transformations.size(), {true});
bool need_sort = false;
// actualize transformation matrices
for (const ModelVolume *volume : object->volumes) {
for (const ModelVolume *volume : object.volumes) {
if (skip != nullptr && skip->skip(volume->id().id)) continue;
const Transform3d &volume_tr = volume->get_matrix();
for (const ModelInstance *instance : object->instances) {
for (const ModelInstance *instance : object.instances) {
const Transform3d &instrance_tr = instance->get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
TrKey key = priv::create_key(volume, instance);
TrKey key = priv::create_key(*volume, *instance);
auto item = priv::find(m_transformations, key);
if (item != m_transformations.end()) {
// actualize transformation all the time
@ -56,9 +56,9 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Mes
std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower);
}
void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, Meshes *meshes)
void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes)
{
const ModelVolumePtrs &volumes = instance->get_object()->volumes;
const ModelVolumePtrs &volumes = instance.get_object()->volumes;
// actualize MeshRaycaster
priv::actualize(m_meshes, volumes, skip, meshes);
@ -72,9 +72,9 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip,
if (skip != nullptr && skip->skip(volume->id().id))
continue;
const Transform3d &volume_tr = volume->get_matrix();
const Transform3d &instrance_tr = instance->get_matrix();
const Transform3d &instrance_tr = instance.get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
TrKey key = priv::create_key(volume, instance);
TrKey key = priv::create_key(*volume, instance);
auto item = priv::find(m_transformations, key);
if (item != m_transformations.end()) {
// actualize transformation all the time

View File

@ -86,8 +86,8 @@ public:
/// <param name="object">Model representation</param>
/// <param name="skip">Condifiton for skip actualization</param>
/// <param name="meshes">Speed up for already created AABBtrees</param>
void actualize(const ModelObject *object, const ISkip *skip = nullptr, Meshes *meshes = nullptr);
void actualize(const ModelInstance *instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr);
void actualize(const ModelObject &object, const ISkip *skip = nullptr, Meshes *meshes = nullptr);
void actualize(const ModelInstance &instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr);
class SkipVolume: public ISkip
{