Cut: Add connectors. WIP

This commit is contained in:
YuSanka 2022-03-08 14:10:25 +01:00
parent 8f70b6e6dc
commit 0fba32fa53
4 changed files with 375 additions and 59 deletions

View File

@ -219,6 +219,46 @@ private:
friend class ModelObject; friend class ModelObject;
}; };
struct CutConnector
{
Vec3f pos;
Vec3f normal;
float radius;
float height;
bool failed = false;
CutConnector()
: pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f)
{}
CutConnector(Vec3f p, Vec3f n, float r, float h, bool fl = false)
: pos(p), normal(n), radius(r), height(h), failed(fl)
{}
CutConnector(const CutConnector& rhs) :
CutConnector(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {}
bool operator==(const CutConnector& sp) const;
bool operator!=(const CutConnector& sp) const { return !(sp == (*this)); }
/*
bool is_inside(const Vec3f& pt) const;
bool get_intersections(const Vec3f& s, const Vec3f& dir,
std::array<std::pair<float, Vec3d>, 2>& out) const;
indexed_triangle_set to_mesh() const;
*/
template<class Archive> inline void serialize(Archive& ar)
{
ar(pos, normal, radius, height, failed);
}
static constexpr size_t steps = 32;
};
using CutConnectors = std::vector<CutConnector>;
// Declared outside of ModelVolume, so it could be forward declared. // Declared outside of ModelVolume, so it could be forward declared.
enum class ModelVolumeType : int { enum class ModelVolumeType : int {
INVALID = -1, INVALID = -1,
@ -269,6 +309,9 @@ public:
// Holes to be drilled into the object so resin can flow out // Holes to be drilled into the object so resin can flow out
sla::DrainHoles sla_drain_holes; sla::DrainHoles sla_drain_holes;
// Connectors to be added into the object after cut
CutConnectors cut_connectors;
/* This vector accumulates the total translation applied to the object by the /* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment to new volumes before adding them to this object in order to preserve alignment

View File

@ -171,7 +171,7 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
if (!m_grabbers.empty()) { if (!m_grabbers.empty()) {
if (m_hover_id < int(m_grabbers.size())) if (m_hover_id < int(m_grabbers.size()))
m_grabbers[m_hover_id].dragging = true; m_grabbers[m_hover_id].dragging = true;
else if (m_group_id >= 0 && m_hover_id >= m_group_id) else if (m_group_id >= 0 && m_hover_id < int(m_grabbers.size() + m_group_id))
m_grabbers[m_hover_id - m_group_id].dragging = true; m_grabbers[m_hover_id - m_group_id].dragging = true;
} }

View File

@ -33,6 +33,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
{ {
m_rotation_gizmo.use_only_grabbers(); m_rotation_gizmo.use_only_grabbers();
m_group_id = 3; m_group_id = 3;
m_connectors_group_id = 4;
m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid")
// , _u8L("Radial"), _u8L("Modular") // , _u8L("Radial"), _u8L("Modular")
@ -50,6 +51,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
}; };
m_axis_names = { "X", "Y", "Z" }; m_axis_names = { "X", "Y", "Z" };
update_connector_shape();
} }
std::string GLGizmoCut3D::get_tooltip() const std::string GLGizmoCut3D::get_tooltip() const
@ -68,11 +71,71 @@ std::string GLGizmoCut3D::get_tooltip() const
bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
{ {
if (mouse_event.Moving())
return false;
if (m_rotation_gizmo.on_mouse(mouse_event)) { if (m_rotation_gizmo.on_mouse(mouse_event)) {
update_clipper(); update_clipper();
return true; return true;
} }
return use_grabbers(mouse_event);
if (use_grabbers(mouse_event))
return true;
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
static bool pending_right_up = false;
if (mouse_event.LeftDown()) {
bool grabber_contains_mouse = (get_hover_id() != -1);
bool control_down = mouse_event.CmdDown();
if ((!control_down || grabber_contains_mouse) &&
gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false))
return true;
}
else if (mouse_event.Dragging()) {
bool control_down = mouse_event.CmdDown();
if (m_parent.get_move_volume_id() != -1) {
// don't allow dragging objects with the Sla gizmo on
return true;
}
else if (!control_down &&
gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) {
// the gizmo got the event and took some action, no need to do
// anything more here
m_parent.set_as_dirty();
return true;
}
else if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) {
// CTRL has been pressed while already dragging -> stop current action
if (mouse_event.LeftIsDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true);
else if (mouse_event.RightIsDown())
pending_right_up = false;
}
}
else if (mouse_event.LeftUp() && !m_parent.is_mouse_dragging()) {
// in case SLA/FDM gizmo is selected, we just pass the LeftUp event
// and stop processing - neither object moving or selecting is
// suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown());
return true;
}
else if (mouse_event.RightDown()) {
if (m_parent.get_selection().get_object_idx() != -1 &&
gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) {
// we need to set the following right up as processed to avoid showing
// the context menu if the user release the mouse over the object
pending_right_up = true;
// event was taken care of by the SlaSupports gizmo
return true;
}
}
else if (pending_right_up && mouse_event.RightUp()) {
pending_right_up = false;
return true;
}
return false;
} }
void GLGizmoCut3D::shift_cut_z(double delta) void GLGizmoCut3D::shift_cut_z(double delta)
@ -128,7 +191,7 @@ void GLGizmoCut3D::set_center(const Vec3d& center)
update_clipper(); update_clipper();
} }
void GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx) bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx)
{ {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(label); m_imgui->text(label);
@ -163,10 +226,13 @@ void GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std:
ImGui::SetCursorScreenPos(backup_pos); ImGui::SetCursorScreenPos(backup_pos);
ImGui::EndGroup(); ImGui::EndGroup();
bool is_changed = selection_idx != selection_out;
selection_idx = selection_out; selection_idx = selection_out;
return is_changed;
} }
void GLGizmoCut3D::render_double_input(const std::string& label, double& value_in) bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_in)
{ {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(label); m_imgui->text(label);
@ -176,12 +242,14 @@ void GLGizmoCut3D::render_double_input(const std::string& label, double& value_i
double value = value_in; double value = value_in;
if (m_imperial_units) if (m_imperial_units)
value *= ObjectManipulation::mm_to_in; value *= ObjectManipulation::mm_to_in;
double old_val = value;
ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
ImGui::SameLine(); ImGui::SameLine();
m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); m_imgui->text(m_imperial_units ? _L("in") : _L("mm"));
value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0);
return old_val != value;
} }
void GLGizmoCut3D::render_move_center_input(int axis) void GLGizmoCut3D::render_move_center_input(int axis)
@ -271,11 +339,6 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id)
void GLGizmoCut3D::render_cut_plane() void GLGizmoCut3D::render_cut_plane()
{ {
m_c->object_clipper()->render_cut();
if (m_hide_cut_plane)
return;
const BoundingBoxf3 box = bounding_box(); const BoundingBoxf3 box = bounding_box();
const float min_x = box.min.x() - Margin - m_plane_center.x(); const float min_x = box.min.x() - Margin - m_plane_center.x();
@ -349,7 +412,7 @@ void GLGizmoCut3D::render_cut_center_graber()
const BoundingBoxf3 box = bounding_box(); const BoundingBoxf3 box = bounding_box();
Vec3d grabber_center = m_plane_center; Vec3d grabber_center = m_plane_center;
grabber_center[Z] += 10; // Margin grabber_center[Z] += float(box.radius()/2.0); // Margin
rotate_vec3d_around_center(grabber_center, angles, m_plane_center); rotate_vec3d_around_center(grabber_center, angles, m_plane_center);
@ -439,31 +502,50 @@ bool GLGizmoCut3D::on_is_activable() const
void GLGizmoCut3D::on_dragging(const UpdateData& data) void GLGizmoCut3D::on_dragging(const UpdateData& data)
{ {
assert(m_hover_id == m_group_id); if (m_hover_id < m_group_id)
return;
const Vec3d & starting_box_center = m_plane_center; CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
const Vec3d & starting_drag_position = m_grabbers[0].center;
double projection = 0.0;
Vec3d starting_vec = starting_drag_position - starting_box_center; if (m_hover_id == m_group_id) {
if (starting_vec.norm() != 0.0) { const Vec3d& starting_box_center = m_plane_center;
Vec3d mouse_dir = data.mouse_ray.unit_vector(); const Vec3d& starting_drag_position = m_grabbers[0].center;
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position double projection = 0.0;
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - starting_drag_position;
starting_vec.normalize(); Vec3d starting_vec = starting_drag_position - starting_box_center;
// finds projection of the vector along the staring direction if (starting_vec.norm() != 0.0) {
projection = inters_vec.dot(starting_vec); Vec3d mouse_dir = data.mouse_ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - starting_drag_position;
starting_vec.normalize();
// finds projection of the vector along the staring direction
projection = inters_vec.dot(starting_vec);
}
if (wxGetKeyState(WXK_SHIFT))
projection = m_snap_step * (double)std::round(projection / m_snap_step);
// move cut plane center
set_center(starting_box_center + starting_vec * projection);
// move connectors
Vec3f shift = Vec3f(starting_vec.cast<float>() * projection);
for (auto& connector : connectors)
connector.pos += shift;
}
else if (m_hover_id > m_group_id)
{
std::pair<Vec3f, Vec3f> pos_and_normal;
if (!unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos_and_normal))
return;
connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first;
connectors[m_hover_id - m_connectors_group_id].normal = -pos_and_normal.second;
} }
if (wxGetKeyState(WXK_SHIFT))
projection = m_snap_step * (double)std::round(projection / m_snap_step);
set_center(starting_box_center + starting_vec * projection);
} }
void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos)
@ -474,7 +556,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos)
// Clamp the center position of the cut plane to the object's bounding box // Clamp the center position of the cut plane to the object's bounding box
//m_plane_center = Vec3d(std::clamp(center_pos.x(), m_min_pos.x(), m_max_pos.x()), //m_plane_center = Vec3d(std::clamp(center_pos.x(), m_min_pos.x(), m_max_pos.x()),
// std::clamp(center_pos.y(), m_min_pos.y(), m_max_pos.y()), // std::clamp(center_pos.y(), m_min_pos.y(), m_max_pos.y()),
// std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z()))); // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z()));
m_center_offset = m_plane_center - m_bb_center; m_center_offset = m_plane_center - m_bb_center;
} }
@ -513,11 +595,17 @@ void GLGizmoCut3D::on_render()
update_clipper_on_render(); update_clipper_on_render();
} }
render_cut_plane(); render_connectors(false);
render_cut_center_graber();
if (m_mode == CutMode::cutPlanar) { m_c->object_clipper()->render_cut();
if (m_hover_id < m_group_id)
m_rotation_gizmo.render(); if (!m_hide_cut_plane) {
render_cut_plane();
render_cut_center_graber();
if (m_mode == CutMode::cutPlanar) {
if (m_hover_id < m_group_id)
m_rotation_gizmo.render();
}
} }
if (!suppress_update_clipper_on_render) if (!suppress_update_clipper_on_render)
@ -528,6 +616,8 @@ void GLGizmoCut3D::on_render_for_picking()
{ {
m_rotation_gizmo.render_for_picking(); m_rotation_gizmo.render_for_picking();
render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
render_connectors(true);
} }
void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
@ -617,11 +707,23 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
render_connect_type_radio_button(ConnectorType::Plug); render_connect_type_radio_button(ConnectorType::Plug);
render_connect_type_radio_button(ConnectorType::Dowel); render_connect_type_radio_button(ConnectorType::Dowel);
render_combo(_u8L("Style"), m_connector_styles, m_connector_style); if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style))
render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape); update_connector_shape();
if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id))
update_connector_shape();
render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio); CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
render_double_input(_u8L("Size"), m_connector_size); if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio))
for (auto& connector : connectors)
connector.height = float(m_connector_depth_ratio);
if (render_double_input(_u8L("Size"), m_connector_size))
for (auto& connector : connectors)
connector.radius = float(m_connector_size * 0.5);
m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut());
if (m_imgui->button(_L("Reset connectors")))
reset_connectors();
m_imgui->disabled_end();
m_imgui->disabled_end(); m_imgui->disabled_end();
@ -633,7 +735,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
ImGui::Separator(); ImGui::Separator();
m_imgui->checkbox("hide_cut_plane", m_hide_cut_plane); m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane);
//////// ////////
static bool hide_clipped = true; static bool hide_clipped = true;
@ -658,6 +760,87 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
} }
} }
void GLGizmoCut3D::render_connectors(bool picking)
{
const Selection& selection = m_parent.get_selection();
#if ENABLE_GLBEGIN_GLEND_REMOVAL
GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
shader->start_using();
ScopeGuard guard([shader]() { shader->stop_using(); });
#else
GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light");
if (shader)
shader->start_using();
ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
//const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
//const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift()));
// glsafe(::glMultMatrixd(instance_matrix.data()));
ColorRGBA render_color;
const CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
size_t cache_size = connectors.size();
for (size_t i = 0; i < cache_size; ++i) {
const CutConnector& connector = connectors[i];
const bool& point_selected = m_selected[i];
// First decide about the color of the point.
if (picking)
render_color = picking_decode(BASE_ID - i - m_connectors_group_id);
else {
if (size_t(m_hover_id- m_connectors_group_id) == i)
render_color = ColorRGBA::CYAN();
else // neither hover nor picking
render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f);
}
#if ENABLE_GLBEGIN_GLEND_REMOVAL
m_connector_shape.set_color(render_color);
#else
const_cast<GLModel*>(&m_connector_shape)->set_color(-1, render_color);
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
glsafe(::glPushMatrix());
// glsafe(::glTranslatef(connector.pos.x() - m_plane_center.x(), connector.pos.y() - m_plane_center.y(), connector.pos.z() - m_plane_center.z()));
glsafe(::glTranslatef(connector.pos.x(), connector.pos.y(), connector.pos.z()));
// glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
if (vol->is_left_handed())
glFrontFace(GL_CW);
const Vec3d& angles = m_rotation_gizmo.get_rotation();
glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0));
glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0));
glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0));
// Matrices set, we can render the point mark now.
/* Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-connector.normal).cast<double>());
Eigen::AngleAxisd aa(q);
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z()));
*/ glsafe(::glTranslated(0., 0., -0.5*connector.height));
glsafe(::glScaled(connector.radius, connector.radius, connector.height));
m_connector_shape.render();
if (vol->is_left_handed())
glFrontFace(GL_CCW);
glsafe(::glPopMatrix());
}
glsafe(::glPopMatrix());
}
bool GLGizmoCut3D::can_perform_cut() const bool GLGizmoCut3D::can_perform_cut() const
{ {
BoundingBoxf3 box = bounding_box(); BoundingBoxf3 box = bounding_box();
@ -684,46 +867,125 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
Vec3d cut_center_offset = m_plane_center - instance_offset; Vec3d cut_center_offset = m_plane_center - instance_offset;
cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); cut_center_offset[Z] -= first_glvolume->get_sla_shift_z();
if (0.0 < object_cut_z && can_perform_cut()) if (0.0 < object_cut_z && can_perform_cut()) {
wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(),
only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) |
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower));
m_selected.clear();
}
else { else {
// the object is SLA-elevated and the plane is under it. // the object is SLA-elevated and the plane is under it.
} }
} }
bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
// Return false if no intersection was found, true otherwise.
bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair<Vec3f, Vec3f>& pos_and_normal)
{ {
if (is_dragging()) if (!m_c->raycaster()->raycaster())
return false; return false;
const ModelObject *mo = m_c->selection_info()->model_object();
const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; const ModelObject* mo = m_c->selection_info()->model_object();
const Transform3d instance_trafo = mi->get_transformation().get_matrix(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()];
const Transform3d instance_trafo = mi->get_transformation().get_matrix();
const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true);
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
int mesh_id = -1; int mesh_id = -1;
for (const ModelVolume *mv : mo->volumes) { for (const ModelVolume* mv : mo->volumes) {
++mesh_id; ++mesh_id;
if (! mv->is_model_part()) if (!mv->is_model_part())
continue; continue;
Vec3f hit; Vec3f hit;
Vec3f normal; Vec3f normal;
bool clipping_plane_was_hit = false; bool clipping_plane_was_hit = false;
m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(),
camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), camera, hit, normal, m_c->object_clipper()->get_clipping_plane(),
nullptr, &clipping_plane_was_hit); nullptr, &clipping_plane_was_hit);
if (clipping_plane_was_hit) { if (clipping_plane_was_hit) {
// The clipping plane was clicked, hit containts coordinates of the hit in world coords. // Return both the point and the facet normal.
std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; pos_and_normal = std::make_pair(hit, normal);
return true; return true;
} }
} }
return false; return false;
} }
void GLGizmoCut3D::reset_connectors()
{
m_c->selection_info()->model_object()->cut_connectors.clear();
m_selected.clear();
}
void GLGizmoCut3D::update_connector_shape()
{
if (m_connector_shape.is_initialized())
m_connector_shape.reset();
bool is_prizm = m_connector_style == size_t(Prizm);
const std::function<indexed_triangle_set(double, double, double)>& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone;
switch (ConnectorShape(m_connector_shape_id)) {
case Triangle:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 3)));
break;
case Square:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 4)));
break;
case Circle:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, 2 * PI / 360));
break;
case Hexagon:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 6)));
break;
}
}
bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{
if (is_dragging() || action != SLAGizmoEventType::LeftDown)
return false;
ModelObject *mo = m_c->selection_info()->model_object();
const Camera& camera = wxGetApp().plater()->get_camera();
int mesh_id = -1;
// left down without selection rectangle - place point on the mesh:
if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down) {
// If any point is in hover state, this should initiate its move - return control back to GLCanvas:
if (m_hover_id != -1)
return false;
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos_and_normal)) {
const Vec3f& hit = pos_and_normal.first;
const Vec3f& normal = pos_and_normal.second;
// The clipping plane was clicked, hit containts coordinates of the hit in world coords.
std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin"));
mo->cut_connectors.emplace_back(hit, -normal, float(m_connector_size * 0.5), float(m_connector_depth_ratio));
m_selected.push_back(false);
assert(m_selected.size() == mo->cut_connectors.size());
m_parent.set_as_dirty();
m_wait_for_up_event = true;
return true;
}
return false;
}
return true;
}
return false;
}
CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const { CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const {
return CommonGizmosDataID( return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo) int(CommonGizmosDataID::SelectionInfo)

View File

@ -18,6 +18,7 @@ class GLGizmoCut3D : public GLGizmoBase
{ {
GLGizmoRotate3D m_rotation_gizmo; GLGizmoRotate3D m_rotation_gizmo;
double m_snap_step{ 1.0 }; double m_snap_step{ 1.0 };
int m_connectors_group_id;
Vec3d m_plane_center{ Vec3d::Zero() }; Vec3d m_plane_center{ Vec3d::Zero() };
// data to check position of the cut palne center on gizmo activation // data to check position of the cut palne center on gizmo activation
@ -26,6 +27,7 @@ class GLGizmoCut3D : public GLGizmoBase
Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_bb_center{ Vec3d::Zero() };
Vec3d m_center_offset{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() };
GLModel m_connector_shape;
#if ENABLE_GLBEGIN_GLEND_REMOVAL #if ENABLE_GLBEGIN_GLEND_REMOVAL
GLModel m_plane; GLModel m_plane;
@ -39,14 +41,18 @@ class GLGizmoCut3D : public GLGizmoBase
bool m_hide_cut_plane{ false }; bool m_hide_cut_plane{ false };
double m_connector_depth_ratio{ 1.5 }; double m_connector_depth_ratio{ 5.0 };
double m_connector_size{ 5.0 }; double m_connector_size{ 2.0 };
float m_label_width{ 150.0 }; float m_label_width{ 150.0 };
float m_control_width{ 200.0 }; float m_control_width{ 200.0 };
bool m_imperial_units{ false }; bool m_imperial_units{ false };
bool suppress_update_clipper_on_render{false}; bool suppress_update_clipper_on_render{false};
mutable std::vector<bool> m_selected; // which pins are currently selected
bool m_selection_empty = true;
bool m_wait_for_up_event = false;
Matrix3d m_rotation_matrix; Matrix3d m_rotation_matrix;
Vec3d m_rotations{ Vec3d::Zero() }; Vec3d m_rotations{ Vec3d::Zero() };
@ -95,7 +101,7 @@ class GLGizmoCut3D : public GLGizmoBase
size_t m_connector_style{ size_t(Prizm) }; size_t m_connector_style{ size_t(Prizm) };
std::vector<std::string> m_connector_shapes; std::vector<std::string> m_connector_shapes;
size_t m_connector_shape{ size_t(Hexagon) }; size_t m_connector_shape_id{ size_t(Hexagon) };
std::vector<std::string> m_axis_names; std::vector<std::string> m_axis_names;
@ -103,6 +109,7 @@ public:
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
std::string get_tooltip() const override; std::string get_tooltip() const override;
bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
/// <summary> /// <summary>
@ -136,13 +143,15 @@ protected:
private: private:
void set_center(const Vec3d& center); void set_center(const Vec3d& center);
void render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx); bool render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx);
void render_double_input(const std::string& label, double& value_in); bool render_double_input(const std::string& label, double& value_in);
void render_move_center_input(int axis); void render_move_center_input(int axis);
void render_rotation_input(int axis); void render_rotation_input(int axis);
void render_connect_mode_radio_button(ConnectorMode mode); void render_connect_mode_radio_button(ConnectorMode mode);
bool render_revert_button(const std::string& label); bool render_revert_button(const std::string& label);
void render_connect_type_radio_button(ConnectorType type); void render_connect_type_radio_button(ConnectorType type);
void render_connectors(bool picking);
bool can_perform_cut() const; bool can_perform_cut() const;
void render_cut_plane(); void render_cut_plane();
@ -150,6 +159,8 @@ private:
void perform_cut(const Selection& selection); void perform_cut(const Selection& selection);
void set_center_pos(const Vec3d& center_pos); void set_center_pos(const Vec3d& center_pos);
bool update_bb(); bool update_bb();
void reset_connectors();
void update_connector_shape();
}; };
} // namespace GUI } // namespace GUI