mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 21:55:59 +08:00
Cut: Add connectors. WIP
This commit is contained in:
parent
8f70b6e6dc
commit
0fba32fa53
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user