mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-13 23:15:58 +08:00
Merge branch 'fs_emboss_temp' into fs_emboss
This commit is contained in:
commit
5159e2b2c0
@ -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);
|
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)
|
float up_limit)
|
||||||
{
|
{
|
||||||
// up and emboss direction for generated model
|
// 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();
|
Vec3d wanted_up_side = Vec3d::UnitZ();
|
||||||
if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY();
|
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
|
// 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
|
// create perpendicular unit vector to surface triangle normal vector
|
||||||
// lay on surface of triangle and define up vector for text
|
// 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_up_side)
|
||||||
.cross(wanted_emboss_dir);
|
.cross(normal);
|
||||||
// normal3d is NOT perpendicular to normal_up_dir
|
// normal3d is NOT perpendicular to normal_up_dir
|
||||||
wanted_up_dir.normalize();
|
wanted_up_dir.normalize();
|
||||||
|
|
||||||
// perpendicular to emboss vector of text and normal
|
// perpendicular to emboss vector of text and normal
|
||||||
Vec3d axis_view;
|
Vec3d axis_view;
|
||||||
double angle_view;
|
double angle_view;
|
||||||
if (wanted_emboss_dir == -Vec3d::UnitZ()) {
|
if (normal == -Vec3d::UnitZ()) {
|
||||||
// text_emboss_dir has opposit direction to wanted_emboss_dir
|
// text_emboss_dir has opposit direction to wanted_emboss_dir
|
||||||
axis_view = Vec3d::UnitY();
|
axis_view = Vec3d::UnitY();
|
||||||
angle_view = M_PI;
|
angle_view = M_PI;
|
||||||
} else {
|
} else {
|
||||||
axis_view = text_emboss_dir.cross(wanted_emboss_dir);
|
axis_view = text_emboss_dir.cross(normal);
|
||||||
angle_view = std::acos(text_emboss_dir.dot(wanted_emboss_dir)); // in rad
|
angle_view = std::acos(text_emboss_dir.dot(normal)); // in rad
|
||||||
axis_view.normalize();
|
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);
|
Eigen::AngleAxis up_rot(angle_up, text_emboss_dir);
|
||||||
|
|
||||||
Transform3d transform = Transform3d::Identity();
|
Transform3d transform = Transform3d::Identity();
|
||||||
transform.translate(position.cast<double>());
|
transform.translate(position);
|
||||||
transform.rotate(view_rot);
|
transform.rotate(view_rot);
|
||||||
transform.rotate(up_rot);
|
transform.rotate(up_rot);
|
||||||
return transform;
|
return transform;
|
||||||
|
@ -273,7 +273,15 @@ namespace Emboss
|
|||||||
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
|
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
|
||||||
/// <returns>Projected shape into space</returns>
|
/// <returns>Projected shape into space</returns>
|
||||||
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
|
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>
|
/// <summary>
|
||||||
/// Create transformation for emboss text object to lay on surface point
|
/// Create transformation for emboss text object to lay on surface point
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -282,7 +290,7 @@ namespace Emboss
|
|||||||
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
||||||
/// <returns>Transformation onto surface point</returns>
|
/// <returns>Transformation onto surface point</returns>
|
||||||
Transform3d create_transformation_onto_surface(
|
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
|
class ProjectZ : public IProjection
|
||||||
{
|
{
|
||||||
|
@ -229,13 +229,17 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
|
|||||||
|
|
||||||
GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent);
|
GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent);
|
||||||
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
||||||
// Try to cast ray into scene and find object for add volume
|
if (gl_volume != nullptr) {
|
||||||
if (priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager))
|
// Try to cast ray into scene and find object for add volume
|
||||||
// object found
|
if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager)) {
|
||||||
return;
|
// When model is broken. It could appear that hit miss the object.
|
||||||
|
// So add part near by in simmilar manner as right panel do
|
||||||
// object is not under mouse position soo create object on plater
|
create_volume(volume_type);
|
||||||
priv::start_create_object_job(emboss_data, mouse_pos);
|
}
|
||||||
|
} else {
|
||||||
|
// object is not under mouse position soo create object on plater
|
||||||
|
priv::start_create_object_job(emboss_data, mouse_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Designed for create volume without information of mouse in scene
|
// Designed for create volume without information of mouse in scene
|
||||||
@ -266,8 +270,9 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
|
|||||||
const GLVolume *vol = nullptr;
|
const GLVolume *vol = nullptr;
|
||||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||||
priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol);
|
priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol);
|
||||||
if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) {
|
if (vol == nullptr) {
|
||||||
assert(vol != nullptr);
|
priv::start_create_object_job(emboss_data, screen_center);
|
||||||
|
} else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) {
|
||||||
// in centroid of convex hull is not hit with object
|
// in centroid of convex hull is not hit with object
|
||||||
// soo create transfomation on border of object
|
// soo create transfomation on border of object
|
||||||
|
|
||||||
@ -370,6 +375,7 @@ static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVol
|
|||||||
/// <param name="selection">Containe what is selected</param>
|
/// <param name="selection">Containe what is selected</param>
|
||||||
/// <returns>Slected when only one volume otherwise nullptr</returns>
|
/// <returns>Slected when only one volume otherwise nullptr</returns>
|
||||||
static const GLVolume *get_gl_volume(const Selection &selection);
|
static const GLVolume *get_gl_volume(const Selection &selection);
|
||||||
|
static GLVolume *get_gl_volume(const GLCanvas3D &canvas);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get transformation to world
|
/// Get transformation to world
|
||||||
@ -390,6 +396,7 @@ static void change_window_position(std::optional<ImVec2> &output_window_offset,
|
|||||||
} // namespace priv
|
} // namespace priv
|
||||||
|
|
||||||
const GLVolume *priv::get_gl_volume(const Selection &selection) {
|
const GLVolume *priv::get_gl_volume(const Selection &selection) {
|
||||||
|
// return selection.get_first_volume();
|
||||||
const auto &list = selection.get_volume_idxs();
|
const auto &list = selection.get_volume_idxs();
|
||||||
if (list.size() != 1)
|
if (list.size() != 1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -397,6 +404,19 @@ const GLVolume *priv::get_gl_volume(const Selection &selection) {
|
|||||||
return selection.get_volume(volume_idx);
|
return selection.get_volume(volume_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLVolume *priv::get_gl_volume(const GLCanvas3D &canvas) {
|
||||||
|
const GLVolume *gl_volume = get_gl_volume(canvas.get_selection());
|
||||||
|
if (gl_volume == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
|
||||||
|
for (GLVolume *v : gl_volumes)
|
||||||
|
if (v->composite_id == gl_volume->composite_id)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model)
|
Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model)
|
||||||
{
|
{
|
||||||
if (!gl_volume)
|
if (!gl_volume)
|
||||||
@ -463,14 +483,98 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu
|
|||||||
return nearest_offset;
|
return nearest_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace priv {
|
||||||
|
static bool allign_vec(const Vec3d &z_f, const Vec3d &z_t, Transform3d &rotate)
|
||||||
|
{
|
||||||
|
Vec3d z_f_norm = z_f.normalized();
|
||||||
|
Vec3d z_t_norm = z_t.normalized();
|
||||||
|
double cos_angle = z_t_norm.dot(z_f_norm);
|
||||||
|
|
||||||
|
// Calculate rotation of Z-vectors from current to wanted position
|
||||||
|
rotate = Transform3d::Identity();
|
||||||
|
|
||||||
|
if (cos_angle == 0.) {
|
||||||
|
// check that direction is not same
|
||||||
|
if (z_t_norm.z() > 0.)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// opposit direction of z_t and z_f (a.k.a. angle 180 DEG)
|
||||||
|
rotate = Eigen::AngleAxis(M_PI, Vec3d::UnitX());
|
||||||
|
return true;
|
||||||
|
} else if (cos_angle >= 1. || cos_angle <= -1.) {
|
||||||
|
// bad cas angle value almost zero angle so no rotation
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate only when angle is not zero
|
||||||
|
// Calculate rotation axe from current to wanted inside instance
|
||||||
|
Vec3d axe = z_t_norm.cross(z_f_norm);
|
||||||
|
axe.normalize();
|
||||||
|
double angle = acos(cos_angle);
|
||||||
|
rotate = Eigen::AngleAxis(-angle, axe);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool allign_z(const Vec3d &z_t, Transform3d &rotate)
|
||||||
|
{
|
||||||
|
// Transformed unit vector Z direction (f)rom, (t)o
|
||||||
|
const Vec3d& z_f = Vec3d::UnitZ();
|
||||||
|
return allign_vec(Vec3d::UnitZ(), z_t, rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate scale in world
|
||||||
|
static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
|
||||||
|
{
|
||||||
|
Vec3d from_dir = from * dir;
|
||||||
|
Vec3d to_dir = to * dir;
|
||||||
|
double from_scale_sq = from_dir.squaredNorm();
|
||||||
|
double to_scale_sq = to_dir.squaredNorm();
|
||||||
|
if (is_approx(from_scale_sq, to_scale_sq, 1e-3))
|
||||||
|
return {}; // no scale
|
||||||
|
return sqrt(from_scale_sq / to_scale_sq);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy from branch et_transformation --> Geometry
|
||||||
|
// suggested by @bubnikv
|
||||||
|
void reset_skew(Transform3d& m)
|
||||||
|
{
|
||||||
|
auto new_scale_factor = [](const Matrix3d& s) {
|
||||||
|
return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average
|
||||||
|
};
|
||||||
|
|
||||||
|
const Eigen::JacobiSVD<Matrix3d> svd(m.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||||
|
Matrix3d u = svd.matrixU();
|
||||||
|
Matrix3d v = svd.matrixV();
|
||||||
|
Matrix3d s = svd.singularValues().asDiagonal();
|
||||||
|
|
||||||
|
//Matrix3d mirror;
|
||||||
|
m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
||||||
{
|
{
|
||||||
auto do_move = [&]() {
|
// Fix when leave window during dragging
|
||||||
|
// Fix when click right button
|
||||||
|
if (m_surface_drag.has_value() && !mouse_event.Dragging()) {
|
||||||
// write transformation from UI into model
|
// write transformation from UI into model
|
||||||
m_parent.do_move(L("Surface move"));
|
m_parent.do_move(L("Surface move"));
|
||||||
|
|
||||||
// Update surface by new position
|
// Update surface by new position
|
||||||
if (m_volume->text_configuration->style.prop.use_surface)
|
bool need_process = m_volume->text_configuration->style.prop.use_surface;
|
||||||
|
|
||||||
|
//if (m_surface_drag->y_scale.has_value()) {
|
||||||
|
// m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale);
|
||||||
|
// need_process |= set_height();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (m_surface_drag->z_scale.has_value()) {
|
||||||
|
// m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale);
|
||||||
|
// need_process |= set_depth();
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (need_process)
|
||||||
process();
|
process();
|
||||||
|
|
||||||
// calculate scale
|
// calculate scale
|
||||||
@ -479,30 +583,30 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
|||||||
// allow moving with object again
|
// allow moving with object again
|
||||||
m_parent.enable_moving(true);
|
m_parent.enable_moving(true);
|
||||||
m_surface_drag.reset();
|
m_surface_drag.reset();
|
||||||
};
|
|
||||||
|
|
||||||
if (mouse_event.Moving()) {
|
// only left up is correct
|
||||||
// Fix when leave window during dragging and move cursor back
|
// otherwise it is fix state and return false
|
||||||
if (m_surface_drag.has_value())
|
return mouse_event.LeftUp();
|
||||||
do_move();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mouse_event.Moving())
|
||||||
|
return false;
|
||||||
|
|
||||||
// detect start text dragging
|
// detect start text dragging
|
||||||
if (mouse_event.LeftDown()) {
|
if (mouse_event.LeftDown()) {
|
||||||
// exist selected volume?
|
// exist selected volume?
|
||||||
if (m_volume == nullptr)
|
if (m_volume == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// must exist hover object
|
if (m_parent.get_first_hover_volume_idx() < 0)
|
||||||
int hovered_id = m_parent.get_first_hover_volume_idx();
|
|
||||||
if (hovered_id < 0)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id];
|
GLVolume *gl_volume = priv::get_gl_volume(m_parent);
|
||||||
const ModelObjectPtrs &objects = m_parent.get_model()->objects;
|
if (gl_volume == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
// hovered object must be actual text volume
|
// hovered object must be actual text volume
|
||||||
|
const ModelObjectPtrs &objects = m_parent.get_model()->objects;
|
||||||
if (m_volume != priv::get_model_volume(gl_volume, objects))
|
if (m_volume != priv::get_model_volume(gl_volume, objects))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -535,8 +639,16 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
|||||||
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
|
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
|
||||||
Vec2d mouse_pos = mouse_coord.cast<double>();
|
Vec2d mouse_pos = mouse_coord.cast<double>();
|
||||||
Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume);
|
Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume);
|
||||||
Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse();
|
Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix();
|
||||||
m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, gl_volume, condition};
|
TextConfiguration &tc = *m_volume->text_configuration;
|
||||||
|
// fix baked transformation from .3mf store process
|
||||||
|
if (tc.fix_3mf_tr.has_value())
|
||||||
|
volume_tr = volume_tr * tc.fix_3mf_tr->inverse();
|
||||||
|
|
||||||
|
Transform3d instance_tr = gl_volume->get_instance_transformation().get_matrix();
|
||||||
|
Transform3d instance_tr_inv = instance_tr.inverse();
|
||||||
|
Transform3d world_tr = instance_tr * volume_tr;
|
||||||
|
m_surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition};
|
||||||
|
|
||||||
// Cancel job to prevent interuption of dragging (duplicit result)
|
// Cancel job to prevent interuption of dragging (duplicit result)
|
||||||
if (m_job_cancel != nullptr)
|
if (m_job_cancel != nullptr)
|
||||||
@ -557,45 +669,70 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
|||||||
Vec2d mouse_pos = mouse_coord.cast<double>();
|
Vec2d mouse_pos = mouse_coord.cast<double>();
|
||||||
Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset;
|
Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset;
|
||||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
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()) {
|
if (!hit.has_value()) {
|
||||||
// cross hair need redraw
|
// cross hair need redraw
|
||||||
m_parent.set_as_dirty();
|
m_parent.set_as_dirty();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate temporary position
|
auto world_linear = m_surface_drag->world.linear();
|
||||||
Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key);
|
// Calculate offset: transformation to wanted position
|
||||||
Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal);
|
{
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
TextConfiguration &tc = *m_volume->text_configuration;
|
Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ()
|
||||||
const FontProp& font_prop = tc.style.prop;
|
auto z_rotation = Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(text_z_world, hit->normal);
|
||||||
apply_transformation(font_prop, trmat);
|
Transform3d world_new = z_rotation * m_surface_drag->world;
|
||||||
|
auto world_new_linear = world_new.linear();
|
||||||
|
|
||||||
|
// 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() = m_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(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value());
|
||||||
|
assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value());
|
||||||
|
|
||||||
|
const TextConfiguration &tc = *m_volume->text_configuration;
|
||||||
// fix baked transformation from .3mf store process
|
// fix baked transformation from .3mf store process
|
||||||
if (tc.fix_3mf_tr.has_value())
|
if (tc.fix_3mf_tr.has_value())
|
||||||
trmat = trmat * (*tc.fix_3mf_tr);
|
volume_new = volume_new * (*tc.fix_3mf_tr);
|
||||||
|
|
||||||
// volume transfomration in world coor
|
// apply move in Z direction for move with flat surface above texture
|
||||||
Transform3d world = object_trmat * trmat;
|
const FontProp &prop = tc.style.prop;
|
||||||
Transform3d volume_tr = m_surface_drag->instance_inv * world;
|
if (!prop.use_surface && prop.distance.has_value()) {
|
||||||
|
Vec3d translate = Vec3d::UnitZ() * (*prop.distance);
|
||||||
|
volume_new.translate(translate);
|
||||||
|
}
|
||||||
|
|
||||||
// Update transformation inside of instances
|
// Update transformation for all instances
|
||||||
for (GLVolume *vol : m_parent.get_volumes().volumes) {
|
for (GLVolume *vol : m_parent.get_volumes().volumes) {
|
||||||
if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() ||
|
if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() ||
|
||||||
vol->volume_idx() != m_surface_drag->gl_volume->volume_idx())
|
vol->volume_idx() != m_surface_drag->gl_volume->volume_idx())
|
||||||
continue;
|
continue;
|
||||||
vol->set_volume_transformation(volume_tr);
|
vol->set_volume_transformation(volume_new);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate scale
|
// update scale of selected volume --> should be approx the same
|
||||||
calculate_scale();
|
calculate_scale();
|
||||||
|
|
||||||
m_parent.set_as_dirty();
|
m_parent.set_as_dirty();
|
||||||
return true;
|
return true;
|
||||||
} else if (mouse_event.LeftUp()) {
|
|
||||||
do_move();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -774,7 +911,13 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
ImVec2 center(
|
ImVec2 center(
|
||||||
mouse_pos.x + m_surface_drag->mouse_offset.x(),
|
mouse_pos.x + m_surface_drag->mouse_offset.x(),
|
||||||
mouse_pos.y + m_surface_drag->mouse_offset.y());
|
mouse_pos.y + m_surface_drag->mouse_offset.y());
|
||||||
priv::draw_cross_hair(center);
|
ImU32 color = ImGui::GetColorU32(
|
||||||
|
m_surface_drag->exist_hit ?
|
||||||
|
ImVec4(1.f, 1.f, 1.f, .75f) : // transparent white
|
||||||
|
ImVec4(1.f, .3f, .3f, .75f)
|
||||||
|
); // Warning color
|
||||||
|
const float radius = 16.f;
|
||||||
|
priv::draw_cross_hair(center, radius, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SHOW_FINE_POSITION
|
#ifdef SHOW_FINE_POSITION
|
||||||
@ -1026,19 +1169,25 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration()
|
|||||||
|
|
||||||
EmbossStyles GLGizmoEmboss::create_default_styles()
|
EmbossStyles GLGizmoEmboss::create_default_styles()
|
||||||
{
|
{
|
||||||
wxFont wx_font_normal = *wxNORMAL_FONT;
|
wxFontEnumerator::InvalidateCache();
|
||||||
wxFont wx_font_small = *wxSMALL_FONT;
|
wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding);
|
||||||
|
|
||||||
|
wxFont wx_font_normal = *wxNORMAL_FONT;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
wx_font_normal.SetFaceName("Helvetica");
|
// Set normal font to helvetica when possible
|
||||||
wx_font_small.SetFaceName("Helvetica");
|
for (const wxString &facename : facenames) {
|
||||||
|
if (facename.IsSameAs("Helvetica")) {
|
||||||
|
wx_font_normal = wxFont(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
|
|
||||||
// https://docs.wxwidgets.org/3.0/classwx_font.html
|
// https://docs.wxwidgets.org/3.0/classwx_font.html
|
||||||
// Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT
|
// Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT
|
||||||
EmbossStyles styles = {
|
EmbossStyles styles = {
|
||||||
WxFontUtils::create_emboss_style(wx_font_normal, _u8L("NORMAL")), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)
|
WxFontUtils::create_emboss_style(wx_font_normal, _u8L("NORMAL")), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)
|
||||||
WxFontUtils::create_emboss_style(wx_font_normal, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT.
|
WxFontUtils::create_emboss_style(*wxSMALL_FONT, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT.
|
||||||
WxFontUtils::create_emboss_style(*wxITALIC_FONT, _u8L("ITALIC")), // A font using the wxFONTFAMILY_ROMAN family and wxFONTSTYLE_ITALIC style and of the same size of wxNORMAL_FONT.
|
WxFontUtils::create_emboss_style(*wxITALIC_FONT, _u8L("ITALIC")), // A font using the wxFONTFAMILY_ROMAN family and wxFONTSTYLE_ITALIC style and of the same size of wxNORMAL_FONT.
|
||||||
WxFontUtils::create_emboss_style(*wxSWISS_FONT, _u8L("SWISS")), // A font identic to wxNORMAL_FONT except for the family used which is wxFONTFAMILY_SWISS.
|
WxFontUtils::create_emboss_style(*wxSWISS_FONT, _u8L("SWISS")), // A font identic to wxNORMAL_FONT except for the family used which is wxFONTFAMILY_SWISS.
|
||||||
WxFontUtils::create_emboss_style(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("MODERN")),
|
WxFontUtils::create_emboss_style(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("MODERN")),
|
||||||
@ -1067,7 +1216,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
|
|||||||
|
|
||||||
// No valid style in defult list
|
// No valid style in defult list
|
||||||
// at least one style must contain loadable font
|
// at least one style must contain loadable font
|
||||||
wxArrayString facenames = wxFontEnumerator::GetFacenames(wxFontEncoding::wxFONTENCODING_SYSTEM);
|
|
||||||
wxFont wx_font;
|
wxFont wx_font;
|
||||||
for (const wxString &face : facenames) {
|
for (const wxString &face : facenames) {
|
||||||
wx_font = wxFont(face);
|
wx_font = wxFont(face);
|
||||||
@ -1615,9 +1763,9 @@ void GLGizmoEmboss::draw_text_input()
|
|||||||
auto &ff = m_style_manager.get_font_file_with_cache();
|
auto &ff = m_style_manager.get_font_file_with_cache();
|
||||||
float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale);
|
float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale);
|
||||||
if (imgui_size > StyleManager::max_imgui_font_size)
|
if (imgui_size > StyleManager::max_imgui_font_size)
|
||||||
append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input."));
|
append_warning(_u8L("Too tall"), _u8L("Diminished font height inside text input."));
|
||||||
if (imgui_size < StyleManager::min_imgui_font_size)
|
if (imgui_size < StyleManager::min_imgui_font_size)
|
||||||
append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input."));
|
append_warning(_u8L("Too small"), _u8L("Enlarged font height inside text input."));
|
||||||
if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who);
|
if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2968,6 +3116,31 @@ void GLGizmoEmboss::draw_style_edit() {
|
|||||||
#endif // SHOW_WX_WEIGHT_INPUT
|
#endif // SHOW_WX_WEIGHT_INPUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GLGizmoEmboss::set_height() {
|
||||||
|
float &value = m_style_manager.get_style().prop.size_in_mm;
|
||||||
|
|
||||||
|
// size can't be zero or negative
|
||||||
|
priv::Limits::apply(value, priv::limits.size_in_mm);
|
||||||
|
|
||||||
|
if (m_volume == nullptr || !m_volume->text_configuration.has_value()) {
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only different value need process
|
||||||
|
if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// store font size into path serialization
|
||||||
|
const std::optional<wxFont> &wx_font_opt = m_style_manager.get_wx_font();
|
||||||
|
if (wx_font_opt.has_value()) {
|
||||||
|
wxFont wx_font = *wx_font_opt;
|
||||||
|
wx_font.SetPointSize(static_cast<int>(value));
|
||||||
|
m_style_manager.set_wx_font(wx_font);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::draw_height(bool use_inch)
|
void GLGizmoEmboss::draw_height(bool use_inch)
|
||||||
{
|
{
|
||||||
float &value = m_style_manager.get_style().prop.size_in_mm;
|
float &value = m_style_manager.get_style().prop.size_in_mm;
|
||||||
@ -2976,24 +3149,21 @@ void GLGizmoEmboss::draw_height(bool use_inch)
|
|||||||
const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm");
|
const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm");
|
||||||
const std::string revert_text_size = _u8L("Revert text size.");
|
const std::string revert_text_size = _u8L("Revert text size.");
|
||||||
const std::string& name = m_gui_cfg->translations.size;
|
const std::string& name = m_gui_cfg->translations.size;
|
||||||
if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) {
|
if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height))
|
||||||
// size can't be zero or negative
|
if (set_height())
|
||||||
priv::Limits::apply(value, priv::limits.size_in_mm);
|
|
||||||
// only different value need process
|
|
||||||
if (!is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) {
|
|
||||||
// store font size into path
|
|
||||||
EmbossStyle &style = m_style_manager.get_style();
|
|
||||||
if (style.type == WxFontUtils::get_actual_type()) {
|
|
||||||
const std::optional<wxFont> &wx_font_opt = m_style_manager.get_wx_font();
|
|
||||||
if (wx_font_opt.has_value()) {
|
|
||||||
wxFont wx_font = *wx_font_opt;
|
|
||||||
wx_font.SetPointSize(static_cast<int>(value));
|
|
||||||
m_style_manager.set_wx_font(wx_font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
bool GLGizmoEmboss::set_depth()
|
||||||
|
{
|
||||||
|
float &value = m_style_manager.get_style().prop.emboss;
|
||||||
|
|
||||||
|
// size can't be zero or negative
|
||||||
|
priv::Limits::apply(value, priv::limits.emboss);
|
||||||
|
|
||||||
|
// only different value need process
|
||||||
|
return !is_approx(value, m_volume->text_configuration->style.prop.emboss);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::draw_depth(bool use_inch)
|
void GLGizmoEmboss::draw_depth(bool use_inch)
|
||||||
@ -3004,11 +3174,9 @@ void GLGizmoEmboss::draw_depth(bool use_inch)
|
|||||||
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
|
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
|
||||||
const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm");
|
const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm");
|
||||||
const std::string name = m_gui_cfg->translations.depth;
|
const std::string name = m_gui_cfg->translations.depth;
|
||||||
if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth)) {
|
if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth))
|
||||||
// size can't be zero or negative
|
if (set_depth())
|
||||||
priv::Limits::apply(value, priv::limits.emboss);
|
process();
|
||||||
process();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3126,21 +3294,33 @@ std::optional<Vec3d> priv::calc_surface_offset(const ModelVolume &volume, Raycas
|
|||||||
std::optional<RaycastManager::Hit> hit_opt = raycast_manager.unproject(point, direction, &cond);
|
std::optional<RaycastManager::Hit> hit_opt = raycast_manager.unproject(point, direction, &cond);
|
||||||
|
|
||||||
// Try to find closest point when no hit object in emboss direction
|
// Try to find closest point when no hit object in emboss direction
|
||||||
if (!hit_opt.has_value())
|
if (!hit_opt.has_value()) {
|
||||||
hit_opt = raycast_manager.closest(point);
|
std::optional<RaycastManager::ClosePoint> close_point_opt = raycast_manager.closest(point);
|
||||||
|
|
||||||
// It should NOT appear. Closest point always exists.
|
// It should NOT appear. Closest point always exists.
|
||||||
if (!hit_opt.has_value())
|
assert(close_point_opt.has_value());
|
||||||
return {};
|
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
|
// It is no neccesary to move with origin by very small value
|
||||||
if (hit_opt->squared_distance < EPSILON)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const RaycastManager::Hit &hit = *hit_opt;
|
const RaycastManager::Hit &hit = *hit_opt;
|
||||||
Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key);
|
if (hit.squared_distance < EPSILON)
|
||||||
Vec3d hit_world = hit_tr * hit.position.cast<double>();
|
return {};
|
||||||
Vec3d offset_world = hit_world - point; // vector in world
|
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
|
// TIP: It should be close to only z move
|
||||||
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
|
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
|
||||||
return offset_volume;
|
return offset_volume;
|
||||||
@ -3376,6 +3556,21 @@ void GLGizmoEmboss::draw_advanced()
|
|||||||
ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str());
|
ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(_u8L("Reset scale").c_str())) {
|
||||||
|
GLVolume *gl_volume = priv::get_gl_volume(m_parent);
|
||||||
|
if (gl_volume != nullptr) {
|
||||||
|
//Transform3d w = gl_volume->world_matrix();
|
||||||
|
//priv::reset_skew_respect_z(w);
|
||||||
|
//Transform3d i = gl_volume->get_instance_transformation().get_matrix();
|
||||||
|
//Transform3d v_new = i.inverse() * w;
|
||||||
|
//gl_volume->set_volume_transformation(v_new);
|
||||||
|
//m_parent.do_move(L("Reset scale"));
|
||||||
|
}
|
||||||
|
} else if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ALLOW_DEBUG_MODE
|
#ifdef ALLOW_DEBUG_MODE
|
||||||
ImGui::Text("family = %s", (font_prop.family.has_value() ?
|
ImGui::Text("family = %s", (font_prop.family.has_value() ?
|
||||||
font_prop.family->c_str() :
|
font_prop.family->c_str() :
|
||||||
@ -3841,7 +4036,9 @@ GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) {
|
|||||||
bool priv::start_create_volume_on_surface_job(
|
bool priv::start_create_volume_on_surface_job(
|
||||||
DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster)
|
DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster)
|
||||||
{
|
{
|
||||||
|
assert(gl_volume != nullptr);
|
||||||
if (gl_volume == nullptr) return false;
|
if (gl_volume == nullptr) return false;
|
||||||
|
|
||||||
Plater *plater = wxGetApp().plater();
|
Plater *plater = wxGetApp().plater();
|
||||||
const ModelObjectPtrs &objects = plater->model().objects;
|
const ModelObjectPtrs &objects = plater->model().objects;
|
||||||
|
|
||||||
@ -3853,21 +4050,27 @@ bool priv::start_create_volume_on_surface_job(
|
|||||||
raycaster.actualize(obj, &cond);
|
raycaster.actualize(obj, &cond);
|
||||||
|
|
||||||
const Camera &camera = plater->get_camera();
|
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
|
// 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
|
// object. After right click, object is selected and object_idx is set
|
||||||
// also hit must exist. But there is options to add text by object list
|
// also hit must exist. But there is options to add text by object list
|
||||||
if (!hit.has_value()) return false;
|
if (!hit.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key);
|
// priv::reset_skew(hit_to_world);
|
||||||
Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix();
|
Transform3d instance = gl_volume->get_instance_transformation().get_matrix();
|
||||||
|
|
||||||
// Create result volume transformation
|
// Create result volume transformation
|
||||||
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal);
|
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal);
|
||||||
const FontProp &font_prop = emboss_data.text_configuration.style.prop;
|
const FontProp &font_prop = emboss_data.text_configuration.style.prop;
|
||||||
apply_transformation(font_prop, surface_trmat);
|
apply_transformation(font_prop, surface_trmat);
|
||||||
Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat;
|
Transform3d world_new = surface_trmat;
|
||||||
|
|
||||||
|
// Reset skew
|
||||||
|
//priv::reset_skew_respect_z(world_new);
|
||||||
|
|
||||||
|
Transform3d volume_trmat = instance.inverse() * world_new;
|
||||||
start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
|
start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,11 @@ private:
|
|||||||
void draw_height(bool use_inch);
|
void draw_height(bool use_inch);
|
||||||
void draw_depth(bool use_inch);
|
void draw_depth(bool use_inch);
|
||||||
|
|
||||||
|
// call after set m_style_manager.get_style().prop.size_in_mm
|
||||||
|
bool set_height();
|
||||||
|
// call after set m_style_manager.get_style().prop.emboss
|
||||||
|
bool set_depth();
|
||||||
|
|
||||||
bool draw_italic_button();
|
bool draw_italic_button();
|
||||||
bool draw_bold_button();
|
bool draw_bold_button();
|
||||||
void draw_advanced();
|
void draw_advanced();
|
||||||
@ -255,7 +260,7 @@ private:
|
|||||||
std::vector<wxString> bad = {};
|
std::vector<wxString> bad = {};
|
||||||
|
|
||||||
// Configuration of font encoding
|
// Configuration of font encoding
|
||||||
const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
|
static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
|
||||||
|
|
||||||
// Identify if preview texture exists
|
// Identify if preview texture exists
|
||||||
GLuint texture_id = 0;
|
GLuint texture_id = 0;
|
||||||
@ -312,6 +317,9 @@ private:
|
|||||||
// hold screen coor offset of cursor from object center
|
// hold screen coor offset of cursor from object center
|
||||||
Vec2d mouse_offset;
|
Vec2d mouse_offset;
|
||||||
|
|
||||||
|
// Start dragging text transformations to world
|
||||||
|
Transform3d world;
|
||||||
|
|
||||||
// Invers transformation of text volume instance
|
// Invers transformation of text volume instance
|
||||||
// Help convert world transformation to instance space
|
// Help convert world transformation to instance space
|
||||||
Transform3d instance_inv;
|
Transform3d instance_inv;
|
||||||
@ -321,6 +329,8 @@ private:
|
|||||||
|
|
||||||
// condition for raycaster
|
// condition for raycaster
|
||||||
RaycastManager::AllowVolumes condition;
|
RaycastManager::AllowVolumes condition;
|
||||||
|
|
||||||
|
bool exist_hit = true;
|
||||||
};
|
};
|
||||||
// Keep data about dragging only during drag&drop
|
// Keep data about dragging only during drag&drop
|
||||||
std::optional<SurfaceDrag> m_surface_drag;
|
std::optional<SurfaceDrag> m_surface_drag;
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
// rasterization of ExPoly
|
// rasterization of ExPoly
|
||||||
#include "libslic3r/SLA/AGGRaster.hpp"
|
#include "libslic3r/SLA/AGGRaster.hpp"
|
||||||
|
|
||||||
// for get DPI
|
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
|
||||||
#include "slic3r/GUI/MainFrame.hpp"
|
|
||||||
|
|
||||||
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||||
|
|
||||||
// ability to request new frame after finish rendering
|
// ability to request new frame after finish rendering
|
||||||
@ -20,15 +15,15 @@ using namespace Slic3r::GUI;
|
|||||||
using namespace Slic3r::GUI::Emboss;
|
using namespace Slic3r::GUI::Emboss;
|
||||||
|
|
||||||
|
|
||||||
CreateFontStyleImagesJob::CreateFontStyleImagesJob(
|
CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input)
|
||||||
StyleManager::StyleImagesData &&input)
|
: m_input(std::move(input)), m_width(0), m_height(0)
|
||||||
: m_input(std::move(input))
|
|
||||||
{
|
{
|
||||||
assert(m_input.result != nullptr);
|
assert(m_input.result != nullptr);
|
||||||
assert(!m_input.styles.empty());
|
assert(!m_input.styles.empty());
|
||||||
assert(!m_input.text.empty());
|
assert(!m_input.text.empty());
|
||||||
assert(m_input.max_size.x() > 1);
|
assert(m_input.max_size.x() > 1);
|
||||||
assert(m_input.max_size.y() > 1);
|
assert(m_input.max_size.y() > 1);
|
||||||
|
assert(m_input.ppm > 1e-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateFontStyleImagesJob::process(Ctl &ctl)
|
void CreateFontStyleImagesJob::process(Ctl &ctl)
|
||||||
@ -36,7 +31,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
// create shapes and calc size (bounding boxes)
|
// create shapes and calc size (bounding boxes)
|
||||||
std::vector<ExPolygons> name_shapes(m_input.styles.size());
|
std::vector<ExPolygons> name_shapes(m_input.styles.size());
|
||||||
std::vector<double> scales(m_input.styles.size());
|
std::vector<double> scales(m_input.styles.size());
|
||||||
images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
|
m_images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
|
||||||
|
|
||||||
for (auto &item : m_input.styles) {
|
for (auto &item : m_input.styles) {
|
||||||
size_t index = &item - &m_input.styles.front();
|
size_t index = &item - &m_input.styles.front();
|
||||||
@ -44,21 +39,17 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop);
|
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop);
|
||||||
|
|
||||||
// create image description
|
// create image description
|
||||||
StyleManager::StyleImage &image = images[index];
|
StyleManager::StyleImage &image = m_images[index];
|
||||||
BoundingBox &bounding_box = image.bounding_box;
|
BoundingBox &bounding_box = image.bounding_box;
|
||||||
for (ExPolygon &shape : shapes)
|
for (ExPolygon &shape : shapes)
|
||||||
bounding_box.merge(BoundingBox(shape.contour.points));
|
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||||
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||||
|
|
||||||
// calculate conversion from FontPoint to screen pixels by size of font
|
// calculate conversion from FontPoint to screen pixels by size of font
|
||||||
auto mf = wxGetApp().mainframe;
|
|
||||||
// dot per inch for monitor
|
|
||||||
int dpi = get_dpi_for_window(mf);
|
|
||||||
double ppm = dpi / 25.4; // pixel per milimeter
|
|
||||||
const auto &cn = item.prop.collection_number;
|
const auto &cn = item.prop.collection_number;
|
||||||
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
||||||
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
|
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
|
||||||
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm;
|
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * m_input.ppm;
|
||||||
scales[index] = scale;
|
scales[index] = scale;
|
||||||
|
|
||||||
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
||||||
@ -78,30 +69,30 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
|
|
||||||
// arrange bounding boxes
|
// arrange bounding boxes
|
||||||
int offset_y = 0;
|
int offset_y = 0;
|
||||||
width = 0;
|
m_width = 0;
|
||||||
for (StyleManager::StyleImage &image : images) {
|
for (StyleManager::StyleImage &image : m_images) {
|
||||||
image.offset.y() = offset_y;
|
image.offset.y() = offset_y;
|
||||||
offset_y += image.tex_size.y+1;
|
offset_y += image.tex_size.y+1;
|
||||||
if (width < image.tex_size.x)
|
if (m_width < image.tex_size.x)
|
||||||
width = image.tex_size.x;
|
m_width = image.tex_size.x;
|
||||||
}
|
}
|
||||||
height = offset_y;
|
m_height = offset_y;
|
||||||
for (StyleManager::StyleImage &image : images) {
|
for (StyleManager::StyleImage &image : m_images) {
|
||||||
const Point &o = image.offset;
|
const Point &o = image.offset;
|
||||||
const ImVec2 &s = image.tex_size;
|
const ImVec2 &s = image.tex_size;
|
||||||
image.uv0 = ImVec2(o.x() / (double) width,
|
image.uv0 = ImVec2(o.x() / (double) m_width,
|
||||||
o.y() / (double) height);
|
o.y() / (double) m_height);
|
||||||
image.uv1 = ImVec2((o.x() + s.x) / (double) width,
|
image.uv1 = ImVec2((o.x() + s.x) / (double) m_width,
|
||||||
(o.y() + s.y) / (double) height);
|
(o.y() + s.y) / (double) m_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up result
|
// Set up result
|
||||||
pixels = std::vector<unsigned char>(4*width * height, {255});
|
m_pixels = std::vector<unsigned char>(4 * m_width * m_height, {255});
|
||||||
|
|
||||||
// upload sub textures
|
// upload sub textures
|
||||||
for (StyleManager::StyleImage &image : images) {
|
for (StyleManager::StyleImage &image : m_images) {
|
||||||
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
|
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
|
||||||
size_t index = &image - &images.front();
|
size_t index = &image - &m_images.front();
|
||||||
double pixel_dim = SCALING_FACTOR / scales[index];
|
double pixel_dim = SCALING_FACTOR / scales[index];
|
||||||
sla::PixelDim dim(pixel_dim, pixel_dim);
|
sla::PixelDim dim(pixel_dim, pixel_dim);
|
||||||
double gamma = 1.;
|
double gamma = 1.;
|
||||||
@ -110,7 +101,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
|
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
|
||||||
|
|
||||||
// copy rastered data to pixels
|
// copy rastered data to pixels
|
||||||
sla::RasterEncoder encoder = [&offset = image.offset, &pix = pixels, w=width,h=height]
|
sla::RasterEncoder encoder = [&offset = image.offset, &pix = m_pixels, w=m_width,h=m_height]
|
||||||
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
||||||
// bigger value create darker image
|
// bigger value create darker image
|
||||||
unsigned char gray_level = 5;
|
unsigned char gray_level = 5;
|
||||||
@ -142,18 +133,18 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
|
|||||||
glsafe(::glBindTexture(target, tex_id));
|
glsafe(::glBindTexture(target, tex_id));
|
||||||
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||||
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||||
GLint w = width, h=height;
|
GLint w = m_width, h = m_height;
|
||||||
glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type,
|
glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type,
|
||||||
(const void *) pixels.data()));
|
(const void *) m_pixels.data()));
|
||||||
|
|
||||||
// set up texture id
|
// set up texture id
|
||||||
void *texture_id = (void *) (intptr_t) tex_id;
|
void *texture_id = (void *) (intptr_t) tex_id;
|
||||||
for (StyleManager::StyleImage &image : images)
|
for (StyleManager::StyleImage &image : m_images)
|
||||||
image.texture_id = texture_id;
|
image.texture_id = texture_id;
|
||||||
|
|
||||||
// move to result
|
// move to result
|
||||||
m_input.result->styles = std::move(m_input.styles);
|
m_input.result->styles = std::move(m_input.styles);
|
||||||
m_input.result->images = std::move(images);
|
m_input.result->images = std::move(m_images);
|
||||||
|
|
||||||
// bind default texture
|
// bind default texture
|
||||||
GLuint no_texture_id = 0;
|
GLuint no_texture_id = 0;
|
||||||
|
@ -19,11 +19,11 @@ class CreateFontStyleImagesJob : public Job
|
|||||||
|
|
||||||
// Output data
|
// Output data
|
||||||
// texture size
|
// texture size
|
||||||
int width, height;
|
int m_width, m_height;
|
||||||
// texture data
|
// texture data
|
||||||
std::vector<unsigned char> pixels;
|
std::vector<unsigned char> m_pixels;
|
||||||
// descriptors of sub textures
|
// descriptors of sub textures
|
||||||
std::vector<StyleManager::StyleImage> images;
|
std::vector<StyleManager::StyleImage> m_images;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
|
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
|
||||||
|
@ -304,12 +304,15 @@ void StyleManager::init_trunc_names(float max_width) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
|
|
||||||
|
|
||||||
// for access to worker
|
// for access to worker
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
|
||||||
|
// for get DPI
|
||||||
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
#include "slic3r/GUI/MainFrame.hpp"
|
||||||
|
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
|
||||||
|
|
||||||
void StyleManager::init_style_images(const Vec2i &max_size,
|
void StyleManager::init_style_images(const Vec2i &max_size,
|
||||||
const std::string &text)
|
const std::string &text)
|
||||||
{
|
{
|
||||||
@ -361,8 +364,15 @@ void StyleManager::init_style_images(const Vec2i &max_size,
|
|||||||
style.prop
|
style.prop
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mf = wxGetApp().mainframe;
|
||||||
|
// dot per inch for monitor
|
||||||
|
int dpi = get_dpi_for_window(mf);
|
||||||
|
// pixel per milimeter
|
||||||
|
double ppm = dpi / ObjectManipulation::in_to_mm;
|
||||||
|
|
||||||
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
||||||
StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images};
|
StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images, ppm};
|
||||||
queue_job(worker, std::make_unique<CreateFontStyleImagesJob>(std::move(data)));
|
queue_job(worker, std::make_unique<CreateFontStyleImagesJob>(std::move(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +277,9 @@ private:
|
|||||||
|
|
||||||
// place to store result in main thread in Finalize
|
// place to store result in main thread in Finalize
|
||||||
std::shared_ptr<StyleImages> result;
|
std::shared_ptr<StyleImages> result;
|
||||||
|
|
||||||
|
// pixel per milimeter (scaled DPI)
|
||||||
|
double ppm;
|
||||||
};
|
};
|
||||||
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images;
|
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images;
|
||||||
bool m_exist_style_images;
|
bool m_exist_style_images;
|
||||||
|
@ -3,69 +3,42 @@
|
|||||||
|
|
||||||
// include for earn camera
|
// include for earn camera
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
#include "slic3r/GUI/Camera.hpp"
|
#include "slic3r/GUI/CameraUtils.hpp"
|
||||||
|
|
||||||
using namespace Slic3r::GUI;
|
using namespace Slic3r::GUI;
|
||||||
|
|
||||||
namespace priv {
|
namespace priv {
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
// copied from private part of RaycastManager.hpp
|
static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip);
|
||||||
using Raycaster = std::pair<size_t, std::unique_ptr<MeshRaycaster> >;
|
static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id);
|
||||||
// ModelVolume.id
|
static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){
|
||||||
|
return std::make_pair(instance->id().id, volume->id().id); }
|
||||||
using Raycasters = std::vector<Raycaster>;
|
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) {
|
||||||
static void actualize(Raycasters &casters, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip)
|
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) {
|
||||||
// check if volume was removed
|
return is_lower_key(i1.first, i2.first); };
|
||||||
std::vector<bool> removed_casters(casters.size(), {true});
|
|
||||||
// actualize MeshRaycaster
|
|
||||||
for (const ModelVolume *volume : volumes) {
|
|
||||||
size_t oid = volume->id().id;
|
|
||||||
if (skip != nullptr && skip->skip(oid))
|
|
||||||
continue;
|
|
||||||
auto item = std::find_if(casters.begin(), casters.end(),
|
|
||||||
[oid](const Raycaster &it) -> bool { return oid == it.first; });
|
|
||||||
if (item == casters.end()) {
|
|
||||||
// add new raycaster
|
|
||||||
auto raycaster = std::make_unique<MeshRaycaster>(volume->get_mesh_shared_ptr());
|
|
||||||
casters.emplace_back(std::make_pair(oid, std::move(raycaster)));
|
|
||||||
} else {
|
|
||||||
size_t index = item - casters.begin();
|
|
||||||
removed_casters[index] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean other raycasters
|
|
||||||
for (int i = removed_casters.size() - 1; i >= 0; --i)
|
|
||||||
if (removed_casters[i])
|
|
||||||
casters.erase(casters.begin() + i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
|
void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
|
||||||
{
|
{
|
||||||
// actualize MeshRaycaster
|
// actualize MeshRaycaster
|
||||||
priv::actualize(m_raycasters, object->volumes, skip);
|
priv::actualize(m_meshes, object->volumes, skip);
|
||||||
|
|
||||||
// check if inscance was removed
|
// check if inscance was removed
|
||||||
std::vector<bool> removed_transf(m_transformations.size(), {true});
|
std::vector<bool> removed_transf(m_transformations.size(), {true});
|
||||||
|
|
||||||
|
bool need_sort = false;
|
||||||
// actualize transformation matrices
|
// actualize transformation matrices
|
||||||
for (const ModelVolume *volume : object->volumes) {
|
for (const ModelVolume *volume : object->volumes) {
|
||||||
if (skip != nullptr && skip->skip(volume->id().id)) continue;
|
if (skip != nullptr && skip->skip(volume->id().id)) continue;
|
||||||
const Transform3d &volume_tr = volume->get_matrix();
|
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();
|
const Transform3d &instrance_tr = instance->get_matrix();
|
||||||
Transform3d transformation = instrance_tr * volume_tr;
|
Transform3d transformation = instrance_tr * volume_tr;
|
||||||
TrKey tr_key = std::make_pair(instance->id().id, volume->id().id);
|
TrKey key = priv::create_key(volume, instance);
|
||||||
auto item = std::find_if(m_transformations.begin(),
|
auto item = priv::find(m_transformations, key);
|
||||||
m_transformations.end(),
|
|
||||||
[&tr_key](const TrItem &it) -> bool {
|
|
||||||
return it.first == tr_key;
|
|
||||||
});
|
|
||||||
if (item != m_transformations.end()) {
|
if (item != m_transformations.end()) {
|
||||||
// actualize transformation all the time
|
// actualize transformation all the time
|
||||||
item->second = transformation;
|
item->second = transformation;
|
||||||
@ -73,8 +46,8 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
|
|||||||
removed_transf[index] = false;
|
removed_transf[index] = false;
|
||||||
} else {
|
} else {
|
||||||
// add new transformation
|
// add new transformation
|
||||||
m_transformations.emplace_back(
|
m_transformations.emplace_back(std::make_pair(key, transformation));
|
||||||
std::make_pair(tr_key, transformation));
|
need_sort = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,17 +56,21 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
|
|||||||
for (int i = removed_transf.size() - 1; i >= 0; --i)
|
for (int i = removed_transf.size() - 1; i >= 0; --i)
|
||||||
if (removed_transf[i])
|
if (removed_transf[i])
|
||||||
m_transformations.erase(m_transformations.begin() + i);
|
m_transformations.erase(m_transformations.begin() + i);
|
||||||
|
|
||||||
|
if (need_sort)
|
||||||
|
std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) {
|
void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) {
|
||||||
const ModelVolumePtrs &volumes = instance->get_object()->volumes;
|
const ModelVolumePtrs &volumes = instance->get_object()->volumes;
|
||||||
|
|
||||||
// actualize MeshRaycaster
|
// actualize MeshRaycaster
|
||||||
priv::actualize(m_raycasters, volumes, skip);
|
priv::actualize(m_meshes, volumes, skip);
|
||||||
|
|
||||||
// check if inscance was removed
|
// check if inscance was removed
|
||||||
std::vector<bool> removed_transf(m_transformations.size(), {true});
|
std::vector<bool> removed_transf(m_transformations.size(), {true});
|
||||||
|
|
||||||
|
bool need_sort = false;
|
||||||
// actualize transformation matrices
|
// actualize transformation matrices
|
||||||
for (const ModelVolume *volume : volumes) {
|
for (const ModelVolume *volume : volumes) {
|
||||||
if (skip != nullptr && skip->skip(volume->id().id))
|
if (skip != nullptr && skip->skip(volume->id().id))
|
||||||
@ -101,9 +78,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip)
|
|||||||
const Transform3d &volume_tr = volume->get_matrix();
|
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;
|
Transform3d transformation = instrance_tr * volume_tr;
|
||||||
TrKey tr_key = std::make_pair(instance->id().id, volume->id().id);
|
TrKey key = priv::create_key(volume, instance);
|
||||||
auto item = std::find_if(m_transformations.begin(), m_transformations.end(),
|
auto item = priv::find(m_transformations, key);
|
||||||
[&tr_key](const TrItem &it) -> bool { return it.first == tr_key; });
|
|
||||||
if (item != m_transformations.end()) {
|
if (item != m_transformations.end()) {
|
||||||
// actualize transformation all the time
|
// actualize transformation all the time
|
||||||
item->second = transformation;
|
item->second = transformation;
|
||||||
@ -111,7 +87,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip)
|
|||||||
removed_transf[index] = false;
|
removed_transf[index] = false;
|
||||||
} else {
|
} else {
|
||||||
// add new transformation
|
// add new transformation
|
||||||
m_transformations.emplace_back(std::make_pair(tr_key, transformation));
|
m_transformations.emplace_back(std::make_pair(key, transformation));
|
||||||
|
need_sort = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,40 +96,72 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip)
|
|||||||
for (int i = removed_transf.size() - 1; i >= 0; --i)
|
for (int i = removed_transf.size() - 1; i >= 0; --i)
|
||||||
if (removed_transf[i])
|
if (removed_transf[i])
|
||||||
m_transformations.erase(m_transformations.begin() + i);
|
m_transformations.erase(m_transformations.begin() + i);
|
||||||
|
|
||||||
|
if (need_sort)
|
||||||
|
std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<RaycastManager::Hit> RaycastManager::ray_from_camera(
|
||||||
std::optional<RaycastManager::Hit> RaycastManager::unproject(
|
|
||||||
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
|
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) {
|
for (const auto &item : m_transformations) {
|
||||||
const TrKey &key = item.first;
|
const TrKey &key = item.first;
|
||||||
size_t volume_id = key.second;
|
size_t volume_id = key.second;
|
||||||
if (skip != nullptr && skip->skip(volume_id)) continue;
|
if (skip != nullptr && skip->skip(volume_id)) continue;
|
||||||
|
const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id);
|
||||||
|
if (mesh == nullptr) continue;
|
||||||
const Transform3d &transformation = item.second;
|
const Transform3d &transformation = item.second;
|
||||||
auto raycaster_it =
|
|
||||||
std::find_if(m_raycasters.begin(), m_raycasters.end(),
|
|
||||||
[volume_id](const RaycastManager::Raycaster &it)
|
|
||||||
-> bool { return volume_id == it.first; });
|
|
||||||
if (raycaster_it == m_raycasters.end()) continue;
|
|
||||||
const MeshRaycaster &raycaster = *(raycaster_it->second);
|
|
||||||
SurfacePoint surface_point;
|
|
||||||
bool success = raycaster.unproject_on_mesh(
|
|
||||||
mouse_pos, transformation, camera,
|
|
||||||
surface_point.position, surface_point.normal);
|
|
||||||
if (!success) continue;
|
|
||||||
|
|
||||||
Vec3d act_hit_tr = transformation * surface_point.position.cast<double>();
|
Vec3d point;
|
||||||
double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm();
|
Vec3d direction;
|
||||||
if (closest.has_value() &&
|
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
|
||||||
closest->squared_distance < squared_distance)
|
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;
|
continue;
|
||||||
closest = Hit(key, surface_point, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!closest.has_value()) return {};
|
if (result.mesh == nullptr)
|
||||||
return closest;
|
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
|
std::optional<RaycastManager::Hit> RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
|
||||||
@ -160,16 +169,11 @@ std::optional<RaycastManager::Hit> RaycastManager::unproject(const Vec3d &point,
|
|||||||
std::optional<Hit> closest;
|
std::optional<Hit> closest;
|
||||||
for (const auto &item : m_transformations) {
|
for (const auto &item : m_transformations) {
|
||||||
const TrKey &key = item.first;
|
const TrKey &key = item.first;
|
||||||
size_t volume_id = key.second;
|
size_t volume_id = key.second;
|
||||||
if (skip != nullptr && skip->skip(volume_id)) continue;
|
if (skip != nullptr && skip->skip(volume_id)) continue;
|
||||||
const Transform3d &transformation = item.second;
|
const Transform3d &transformation = item.second;
|
||||||
auto raycaster_it =
|
const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id);
|
||||||
std::find_if(m_raycasters.begin(), m_raycasters.end(),
|
if (mesh == nullptr) continue;
|
||||||
[volume_id](const RaycastManager::Raycaster &it)
|
|
||||||
-> bool { return volume_id == it.first; });
|
|
||||||
if (raycaster_it == m_raycasters.end()) continue;
|
|
||||||
const MeshRaycaster &raycaster = *(raycaster_it->second);
|
|
||||||
const AABBMesh& mesh = raycaster.get_aabb_mesh();
|
|
||||||
Transform3d tr_inv = transformation.inverse();
|
Transform3d tr_inv = transformation.inverse();
|
||||||
Vec3d mesh_point = tr_inv * point;
|
Vec3d mesh_point = tr_inv * point;
|
||||||
Vec3d mesh_direction = tr_inv.linear() * direction;
|
Vec3d mesh_direction = tr_inv.linear() * direction;
|
||||||
@ -179,49 +183,50 @@ std::optional<RaycastManager::Hit> RaycastManager::unproject(const Vec3d &point,
|
|||||||
Vec3d point_negative = mesh_point + mesh_direction;
|
Vec3d point_negative = mesh_point + mesh_direction;
|
||||||
|
|
||||||
// Throw ray to both directions of ray
|
// Throw ray to both directions of ray
|
||||||
std::vector<AABBMesh::hit_result> hits = mesh.query_ray_hits(point_positive, mesh_direction);
|
std::vector<AABBMesh::hit_result> hits = mesh->query_ray_hits(point_positive, mesh_direction);
|
||||||
std::vector<AABBMesh::hit_result> hits_neg = mesh.query_ray_hits(point_negative, -mesh_direction);
|
std::vector<AABBMesh::hit_result> 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()));
|
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) {
|
for (const AABBMesh::hit_result &hit : hits) {
|
||||||
double squared_distance = (mesh_point - hit.position()).squaredNorm();
|
double squared_distance = (mesh_point - hit.position()).squaredNorm();
|
||||||
if (closest.has_value() &&
|
if (closest.has_value() &&
|
||||||
closest->squared_distance < squared_distance)
|
closest->squared_distance < squared_distance)
|
||||||
continue;
|
continue;
|
||||||
SurfacePoint surface_point(hit.position().cast<float>(), hit.normal().cast<float>());
|
closest = Hit{{hit.position(), hit.normal()}, key, squared_distance};
|
||||||
closest = Hit(key, surface_point, squared_distance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<RaycastManager::Hit> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const {
|
std::optional<RaycastManager::ClosePoint> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const
|
||||||
std::optional<Hit> closest;
|
{
|
||||||
|
std::optional<ClosePoint> closest;
|
||||||
for (const auto &item : m_transformations) {
|
for (const auto &item : m_transformations) {
|
||||||
const TrKey &key = item.first;
|
const TrKey &key = item.first;
|
||||||
size_t volume_id = key.second;
|
size_t volume_id = key.second;
|
||||||
if (skip != nullptr && skip->skip(volume_id))
|
if (skip != nullptr && skip->skip(volume_id))
|
||||||
continue;
|
continue;
|
||||||
auto raycaster_it = std::find_if(m_raycasters.begin(), m_raycasters.end(),
|
const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id);
|
||||||
[volume_id](const RaycastManager::Raycaster &it) -> bool { return volume_id == it.first; });
|
if (mesh == nullptr) continue;
|
||||||
if (raycaster_it == m_raycasters.end())
|
|
||||||
continue;
|
|
||||||
const MeshRaycaster &raycaster = *(raycaster_it->second);
|
|
||||||
const Transform3d &transformation = item.second;
|
const Transform3d &transformation = item.second;
|
||||||
Transform3d tr_inv = transformation.inverse();
|
Transform3d tr_inv = transformation.inverse();
|
||||||
Vec3d mesh_point_d = tr_inv * point;
|
Vec3d mesh_point = tr_inv * point;
|
||||||
Vec3f mesh_point_f = mesh_point_d.cast<float>();
|
|
||||||
Vec3f n;
|
int face_idx = 0;
|
||||||
Vec3f p = raycaster.get_closest_point(mesh_point_f, &n);
|
Vec3d closest_point;
|
||||||
double squared_distance = (mesh_point_f - p).squaredNorm();
|
Vec3d pointd = point.cast<double>();
|
||||||
|
mesh->squared_distance(pointd, face_idx, closest_point);
|
||||||
|
|
||||||
|
double squared_distance = (mesh_point - closest_point).squaredNorm();
|
||||||
if (closest.has_value() && closest->squared_distance < squared_distance)
|
if (closest.has_value() && closest->squared_distance < squared_distance)
|
||||||
continue;
|
continue;
|
||||||
SurfacePoint surface_point(p,n);
|
|
||||||
closest = Hit(key, surface_point, squared_distance);
|
closest = ClosePoint{key, closest_point, squared_distance};
|
||||||
}
|
}
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const {
|
Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const {
|
||||||
|
// TODO: transformations are sorted use lower bound
|
||||||
auto item = std::find_if(m_transformations.begin(),
|
auto item = std::find_if(m_transformations.begin(),
|
||||||
m_transformations.end(),
|
m_transformations.end(),
|
||||||
[&tr_key](const TrItem &it) -> bool {
|
[&tr_key](const TrItem &it) -> bool {
|
||||||
@ -229,4 +234,58 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons
|
|||||||
});
|
});
|
||||||
if (item == m_transformations.end()) return Transform3d::Identity();
|
if (item == m_transformations.end()) return Transform3d::Identity();
|
||||||
return item->second;
|
return item->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip)
|
||||||
|
{
|
||||||
|
// check if volume was removed
|
||||||
|
std::vector<bool> removed_meshes(meshes.size(), {true});
|
||||||
|
bool need_sort = false;
|
||||||
|
// actualize MeshRaycaster
|
||||||
|
for (const ModelVolume *volume : volumes) {
|
||||||
|
size_t oid = volume->id().id;
|
||||||
|
if (skip != nullptr && skip->skip(oid))
|
||||||
|
continue;
|
||||||
|
auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; });
|
||||||
|
if (item == meshes.end()) {
|
||||||
|
// add new raycaster
|
||||||
|
bool calculate_epsilon = true;
|
||||||
|
auto mesh = std::make_unique<AABBMesh>(volume->mesh(), calculate_epsilon);
|
||||||
|
meshes.emplace_back(std::make_pair(oid, std::move(mesh)));
|
||||||
|
need_sort = true;
|
||||||
|
} else {
|
||||||
|
size_t index = item - meshes.begin();
|
||||||
|
removed_meshes[index] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean other raycasters
|
||||||
|
for (int i = removed_meshes.size() - 1; i >= 0; --i)
|
||||||
|
if (removed_meshes[i])
|
||||||
|
meshes.erase(meshes.begin() + i);
|
||||||
|
|
||||||
|
// All the time meshes must be sorted by volume id - for faster search
|
||||||
|
if (need_sort) {
|
||||||
|
auto is_lower = [](const RaycastManager::Mesh &m1, const RaycastManager::Mesh &m2) { return m1.first < m2.first; };
|
||||||
|
std::sort(meshes.begin(), meshes.end(), is_lower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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; };
|
||||||
|
auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index);
|
||||||
|
if (it == meshes.end() || it->first != volume_id)
|
||||||
|
return nullptr;
|
||||||
|
return &(*(it->second));
|
||||||
|
}
|
||||||
|
|
||||||
|
RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) {
|
||||||
|
auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool {
|
||||||
|
return priv::is_lower_key(it.first, key);
|
||||||
|
};
|
||||||
|
auto it = std::lower_bound(items.begin(), items.end(), key, fnc);
|
||||||
|
if (it == items.end() || it->first != key)
|
||||||
|
return items.end();
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
#define slic3r_RaycastManager_hpp_
|
#define slic3r_RaycastManager_hpp_
|
||||||
|
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <optional> // unique_ptr
|
#include <optional>
|
||||||
#include <map>
|
#include "libslic3r/AABBMesh.hpp" // Structure to cast rays
|
||||||
#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster
|
|
||||||
#include "libslic3r/Point.hpp" // Transform3d
|
#include "libslic3r/Point.hpp" // Transform3d
|
||||||
#include "libslic3r/ObjectID.hpp"
|
#include "libslic3r/ObjectID.hpp"
|
||||||
#include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume
|
#include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume
|
||||||
|
#include "slic3r/GUI/Camera.hpp"
|
||||||
|
|
||||||
namespace Slic3r::GUI{
|
namespace Slic3r::GUI{
|
||||||
|
|
||||||
@ -17,19 +17,22 @@ namespace Slic3r::GUI{
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class RaycastManager
|
class RaycastManager
|
||||||
{
|
{
|
||||||
// ModelVolume.id
|
// Public structures used by RaycastManager
|
||||||
using Raycaster = std::pair<size_t, std::unique_ptr<MeshRaycaster> >;
|
public:
|
||||||
std::vector<Raycaster> m_raycasters;
|
|
||||||
|
|
||||||
// Key for transformation consist of unique volume and instance
|
// ModelVolume.id
|
||||||
|
using Mesh = std::pair<size_t, std::unique_ptr<AABBMesh> >;
|
||||||
|
using Meshes = std::vector<Mesh>;
|
||||||
|
|
||||||
|
// Key for transformation consist of unique volume and instance id ... ObjectId()
|
||||||
// ModelInstance, ModelVolume
|
// ModelInstance, ModelVolume
|
||||||
using TrKey = std::pair<size_t, size_t>;
|
using TrKey = std::pair<size_t, size_t>;
|
||||||
using TrItem = std::pair<TrKey, Transform3d>;
|
using TrItem = std::pair<TrKey, Transform3d>;
|
||||||
std::vector<TrItem> m_transformations;
|
using TrItems = std::vector<TrItem>;
|
||||||
|
|
||||||
// should contain shared pointer to camera but it is not shared pointer so it need it every time when casts rays
|
/// <summary>
|
||||||
|
/// Interface for identify allowed volumes to cast rays.
|
||||||
public:
|
/// </summary>
|
||||||
class ISkip{
|
class ISkip{
|
||||||
public:
|
public:
|
||||||
virtual ~ISkip() = default;
|
virtual ~ISkip() = default;
|
||||||
@ -42,6 +45,39 @@ public:
|
|||||||
virtual bool skip(const size_t &model_volume_id) const { return false; }
|
virtual bool skip(const size_t &model_volume_id) const { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: it is more general object move outside of this class
|
||||||
|
template<typename T>
|
||||||
|
struct SurfacePoint {
|
||||||
|
using Vec3 = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
|
||||||
|
Vec3 position = Vec3::Zero();
|
||||||
|
Vec3 normal = Vec3::UnitZ();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Hit : public SurfacePoint<double>
|
||||||
|
{
|
||||||
|
TrKey tr_key;
|
||||||
|
double squared_distance;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClosePoint
|
||||||
|
{
|
||||||
|
TrKey tr_key;
|
||||||
|
Vec3d point;
|
||||||
|
double squared_distance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Members
|
||||||
|
private:
|
||||||
|
// Keep structure to fast cast rays
|
||||||
|
// meshes are sorted by volume_id for faster search
|
||||||
|
Meshes m_meshes;
|
||||||
|
|
||||||
|
// Keep transformation of meshes
|
||||||
|
TrItems m_transformations;
|
||||||
|
// Note: one mesh could have more transformations ... instances
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Actualize raycasters + transformation
|
/// Actualize raycasters + transformation
|
||||||
/// Detection of removed object
|
/// Detection of removed object
|
||||||
@ -53,27 +89,6 @@ public:
|
|||||||
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
|
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
|
||||||
void actualize(const ModelInstance *instance, const ISkip *skip = nullptr);
|
void actualize(const ModelInstance *instance, const ISkip *skip = nullptr);
|
||||||
|
|
||||||
// TODO: it is more general object move outside of this class
|
|
||||||
struct SurfacePoint
|
|
||||||
{
|
|
||||||
Vec3f position = Vec3f::Zero();
|
|
||||||
Vec3f normal = Vec3f::UnitZ();
|
|
||||||
SurfacePoint() = default;
|
|
||||||
SurfacePoint(Vec3f position, Vec3f normal)
|
|
||||||
: position(position), normal(normal)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Hit: public SurfacePoint
|
|
||||||
{
|
|
||||||
using Key = TrKey;
|
|
||||||
Key tr_key;
|
|
||||||
double squared_distance;
|
|
||||||
Hit(const Key& tr_key, const SurfacePoint& surface_point, double squared_distance)
|
|
||||||
: SurfacePoint(surface_point), tr_key(tr_key), squared_distance(squared_distance)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SkipVolume: public ISkip
|
class SkipVolume: public ISkip
|
||||||
{
|
{
|
||||||
size_t volume_id;
|
size_t volume_id;
|
||||||
@ -95,15 +110,13 @@ public:
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unproject on mesh by Mesh raycasters
|
/// Unproject on mesh by Mesh raycasters
|
||||||
/// Note: Function use current camera position from wxGetApp()
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouse_pos">Position of mouse on screen</param>
|
/// <param name="mouse_pos">Position of mouse on screen</param>
|
||||||
/// <param name="camera">Projection params</param>
|
/// <param name="camera">Projection params</param>
|
||||||
/// <param name="skip">Define which caster will be skipped, null mean no skip</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>
|
/// <returns>Position on surface, normal direction in world coorinate
|
||||||
std::optional<Hit> unproject(const Vec2d &mouse_pos,
|
/// + key, to know hitted instance and volume</returns>
|
||||||
const Camera &camera,
|
std::optional<Hit> ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const;
|
||||||
const ISkip *skip = nullptr) const;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unproject Ray(point direction) on mesh by MeshRaycasters
|
/// Unproject Ray(point direction) on mesh by MeshRaycasters
|
||||||
@ -121,10 +134,10 @@ public:
|
|||||||
/// <param name="point">Point</param>
|
/// <param name="point">Point</param>
|
||||||
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
|
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
std::optional<Hit> closest(const Vec3d &point, const ISkip *skip = nullptr) const;
|
std::optional<ClosePoint> closest(const Vec3d &point, const ISkip *skip = nullptr) const;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Getter on transformation
|
/// Getter on transformation from hitted volume to world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tr_key">Define transformation</param>
|
/// <param name="tr_key">Define transformation</param>
|
||||||
/// <returns>Transformation for key</returns>
|
/// <returns>Transformation for key</returns>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user