Merge branch 'fs_emboss_temp' into fs_emboss

This commit is contained in:
Filip Sykala - NTB T15p 2023-02-17 11:39:45 +01:00
commit 5159e2b2c0
10 changed files with 599 additions and 283 deletions

View File

@ -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;

View File

@ -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
{ {

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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)));
} }

View File

@ -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;

View File

@ -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;
}

View File

@ -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>