Cut: experiment with selecting parts:

Instead of clicking on contours, the user would click on the object itself.
Right mouse button is used currently.
Many loose ends ! Really just an experiment.
This commit is contained in:
Lukas Matena 2023-02-23 01:45:14 +01:00
parent 1c4d43b3a4
commit e5b66f125f
6 changed files with 148 additions and 124 deletions

View File

@ -299,8 +299,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
if (m_was_contour_selected) {
// Following would inform the clipper about the mouse click, so it can
// toggle the respective contour as disabled.
m_c->object_clipper()->pass_mouse_click(pos_world);
process_contours();
//m_c->object_clipper()->pass_mouse_click(pos_world);
//process_contours();
return true;
}
@ -309,7 +309,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
flip_cut_plane();
}
if (!m_cut_by_contour_glmodels.empty())
if (m_part_selection.valid)
m_parent.toggle_model_objects_visibility(false);
return true;
}
@ -351,6 +351,14 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
return true;
}
else if (mouse_event.RightDown()) {
if (! m_connectors_editing) {
// Check the internal part raycasters.
if (! m_part_selection.valid)
process_contours();
m_part_selection.toggle_selection(mouse_pos);
return true;
}
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
@ -446,9 +454,6 @@ void GLGizmoCut3D::update_clipper()
on_register_raycasters_for_picking();
else
update_raycasters_for_picking_transform();
if (!m_c->object_clipper()->has_disable_contour())
reset_cut_by_contours();
}
void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/)
@ -1384,11 +1389,10 @@ void GLGizmoCut3D::render_clipper_cut()
::glEnable(GL_DEPTH_TEST);
}
void GLGizmoCut3D::render_colored_parts()
void GLGizmoCut3D::PartSelection::render(const Vec3d* normal)
{
if (m_cut_by_contour_glmodels.empty())
if (! valid)
return;
assert(m_cut_by_contour_objects[0]->volumes.size() == m_cut_by_contour_glmodels.size());
if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) {
const Camera& camera = wxGetApp().plater()->get_camera();
@ -1396,19 +1400,44 @@ void GLGizmoCut3D::render_colored_parts()
shader->start_using();
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Vec3d inst_offset = m_cut_by_contour_objects[0]->instances[0]->get_offset();
// FIXME: Cache the transforms.
const Vec3d inst_offset = model_object->instances[0]->get_offset();
const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset);
for (size_t id = 0; id < m_cut_by_contour_glmodels.size(); id++) {
const Vec3d volume_offset = m_cut_by_contour_objects[0]->volumes[id]->get_offset();
for (size_t id=0; id<parts.size(); ++id) {
if (normal && camera.get_dir_forward().dot(*normal) < 0 && parts[id].selected)
continue;
const Vec3d volume_offset = model_object->volumes[id]->get_offset();
shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset));
m_cut_by_contour_glmodels[id].render();
//parts[id].glmodel.set_color(parts[id].selected ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : ColorRGBA(0.f, 1.f, 0.f, 1.f));
parts[id].glmodel.set_color(parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR);
parts[id].glmodel.render();
}
shader->stop_using();
}
}
void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
{
// FIXME: Cache the transforms.
const Camera& camera = wxGetApp().plater()->get_camera();
Vec3f pos;
Vec3f normal;
for (size_t id=0; id<parts.size(); ++id) {
const Vec3d volume_offset = model_object->volumes[id]->get_offset();
Transform3d tr = model_object->instances.front()->get_matrix() * model_object->volumes[id]->get_matrix();
if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) {
parts[id].selected = ! parts[id].selected;
return;
}
}
}
void GLGizmoCut3D::on_render()
{
if (m_state == On) {
@ -1426,7 +1455,9 @@ void GLGizmoCut3D::on_render()
render_connectors();
if (!m_connectors_editing)
render_colored_parts();
m_part_selection.render();
else
m_part_selection.render(&m_cut_normal);
render_clipper_cut();
@ -1679,60 +1710,68 @@ void GLGizmoCut3D::flip_cut_plane()
update_clipper();
}
void GLGizmoCut3D::reset_cut_by_contours()
GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal)
{
if (!m_cut_by_contour_objects.empty()) {
m_cut_by_contour_objects.clear();
m_cut_by_contour_glmodels.clear();
m_disabled_contours.clear();
model_object = mo; // FIXME: Ownership.
m_parent.toggle_model_objects_visibility(true);
}
}
void GLGizmoCut3D::process_contours()
{
reset_cut_by_contours();
if (!m_c->object_clipper()->has_disable_contour())
return;
const Selection& selection = m_parent.get_selection();
const ModelObjectPtrs& model_objects = selection.get_model()->objects;
wxBusyCursor wait;
const int instance_idx = selection.get_instance_idx();
const int object_idx = selection.get_object_idx();
m_cut_by_contour_objects = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection),
ModelObjectCutAttribute::KeepUpper |
ModelObjectCutAttribute::KeepLower |
ModelObjectCutAttribute::KeepAsParts |
ModelObjectCutAttribute::InvalidateCutInfo);
assert(m_cut_by_contour_objects.size() == 1);
const ModelVolumePtrs& volumes = m_cut_by_contour_objects[0]->volumes;
const ModelVolumePtrs& volumes = mo->volumes;
// split to parts
for (int id = int(volumes.size())-1; id >= 0; id--)
if (volumes[id]->is_splittable())
volumes[id]->split(1);
m_cut_by_contour_glmodels.reserve(volumes.size());
parts.clear();
for (const ModelVolume* volume : volumes) {
assert(volume != nullptr);
parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true});
parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f });
parts.back().glmodel.init_from(volume->mesh());
GLModel glmodel;
// any condition to test
if (volume->name.find("_A") != std::string::npos)
glmodel.set_color({ 0.5f, 0.9f, 0.9f, 0.5f }); // glmodel.set_color(UPPER_PART_COLOR);
else
glmodel.set_color({ 0.9f, 0.5f, 0.9f, 0.5f }); // glmodel.set_color(LOWER_PART_COLOR);
m_cut_by_contour_glmodels.push_back(glmodel);
// Now check whether this part is below or above the plane.
Transform3d tr = (model_object->instances.front()->get_matrix() * volume->get_matrix()).inverse();
Vec3f pos = (tr * center).cast<float>();
Vec3f norm = (tr.linear().inverse().transpose() * normal).cast<float>();
for (const Vec3f& v : volume->mesh().its.vertices) {
double p = (v - pos).dot(norm);
if (std::abs(p) > EPSILON) {
parts.back().selected = p > 0.;
break;
}
}
}
valid = true;
}
void GLGizmoCut3D::reset_cut_by_contours()
{
m_part_selection = PartSelection();
m_parent.toggle_model_objects_visibility(true);
}
void GLGizmoCut3D::process_contours()
{
reset_cut_by_contours();
const Selection& selection = m_parent.get_selection();
const ModelObjectPtrs& model_objects = selection.get_model()->objects;
wxBusyCursor wait;
const int instance_idx = selection.get_instance_idx();
const int object_idx = selection.get_object_idx();
ModelObjectPtrs moptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection),
ModelObjectCutAttribute::KeepUpper |
ModelObjectCutAttribute::KeepLower |
ModelObjectCutAttribute::KeepAsParts |
ModelObjectCutAttribute::InvalidateCutInfo);
assert(moptrs.size() == 1);
m_part_selection = PartSelection(moptrs.front(), m_plane_center, m_cut_normal);
m_parent.toggle_model_objects_visibility(false);
}
void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/)
@ -2061,7 +2100,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
{
// check if connector pos is out of clipping plane
if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos, true)) {
if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(cur_pos) == -1) {
m_info_stats.outside_cut_contour++;
return true;
}
@ -2087,7 +2126,7 @@ bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& co
its_transform(mesh, translation_transform(cur_pos) * m_rotation_m);
for (auto vertex : vertices) {
if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast<double>(), true)) {
if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(vertex.cast<double>()) == -1) {
m_info_stats.outside_cut_contour++;
return true;
}
@ -2327,7 +2366,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po
} else
return false;
if (! m_c->object_clipper()->is_projection_inside_cut(hit, respect_disabled_contour))
if (m_c->object_clipper()->is_projection_inside_cut(hit) == -1)
return false;
// recalculate hit to object's local position
@ -2555,20 +2594,8 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
if (!m_keep_upper || !m_keep_lower)
return false;
if (!m_connectors_editing) {
if (0 && action == SLAGizmoEventType::LeftDown) {
// disable / enable current contour
Vec3d pos;
Vec3d pos_world;
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos, pos_world)) {
// Following would inform the clipper about the mouse click, so it can
// toggle the respective contour as disabled.
m_c->object_clipper()->pass_mouse_click(pos_world);
return true;
}
}
if (!m_connectors_editing)
return false;
}
CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;

View File

@ -137,10 +137,25 @@ class GLGizmoCut3D : public GLGizmoBase
bool m_was_cut_plane_dragged { false };
bool m_was_contour_selected { false };
std::vector<GLModel> m_cut_by_contour_glmodels;
ModelObjectPtrs m_cut_by_contour_objects;
// attributes for disabled contours <pos, norm>
std::vector<std::pair<Vec3d, Vec3d>> m_disabled_contours;
struct PartSelection {
PartSelection() = default;
PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal);
void render(const Vec3d* normal = nullptr);
void toggle_selection(const Vec2d& mouse_pos);
struct Part {
GLModel glmodel;
MeshRaycaster raycaster;
bool selected;
bool upper;
};
ModelObject* model_object; // FIXME: Ownership !
std::vector<Part> parts;
bool valid = false;
};
PartSelection m_part_selection;
bool m_show_shortcuts{ false };
std::vector<std::pair<wxString, wxString>> m_shortcuts;
@ -299,7 +314,6 @@ private:
void init_picking_models();
void init_rendering_items();
void render_clipper_cut();
void render_colored_parts();
void clear_selection();
void reset_connectors();
void init_connector_shapes();

View File

@ -388,10 +388,26 @@ void ObjectClipper::render_cut() const
}
}
bool ObjectClipper::is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const
int ObjectClipper::get_number_of_contours() const
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(),
[point, respect_disabled_contour](const auto& cl) { return cl.first->is_projection_inside_cut(point, respect_disabled_contour); });
int sum = 0;
for (const auto& [clipper, trafo] : m_clippers)
sum += clipper->get_number_of_contours();
return sum;
}
int ObjectClipper::is_projection_inside_cut(const Vec3d& point) const
{
if (m_clp_ratio == 0.)
return -1;
int idx_offset = 0;
for (const auto& [clipper, trafo] : m_clippers) {
if (int idx = clipper->is_projection_inside_cut(point); idx != -1)
return idx_offset + idx;
idx_offset += clipper->get_number_of_contours();
}
return -1;
}
bool ObjectClipper::has_valid_contour() const
@ -399,10 +415,6 @@ bool ObjectClipper::has_valid_contour() const
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); });
}
bool ObjectClipper::has_disable_contour() const
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_disable_contour(); });
}
void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal)
{
@ -442,16 +454,6 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou
clipper.first->set_behaviour(fill_cut, contour_width);
}
void ObjectClipper::pass_mouse_click(const Vec3d& pt)
{
for (auto& clipper : m_clippers)
clipper.first->pass_mouse_click(pt);
}
std::vector<Vec3d> ObjectClipper::get_disabled_contours() const
{
return std::vector<Vec3d>();
}
void SupportsClipper::on_update()
{

View File

@ -247,12 +247,10 @@ public:
void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos);
void set_behavior(bool hide_clipped, bool fill_cut, double contour_width);
void pass_mouse_click(const Vec3d& pt);
std::vector<Vec3d> get_disabled_contours() const;
int get_number_of_contours() const;
bool is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const;
int is_projection_inside_cut(const Vec3d& point_in) const;
bool has_valid_contour() const;
bool has_disable_contour() const;
protected:

View File

@ -146,18 +146,19 @@ void MeshClipper::render_contour(const ColorRGBA& color)
curr_shader->start_using();
}
bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const
int MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const
{
if (!m_result || m_result->cut_islands.empty())
return false;
return -1;
Vec3d point = m_result->trafo.inverse() * point_in;
Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y()));
for (const CutIsland& isl : m_result->cut_islands) {
for (int i=0; i<int(m_result->cut_islands.size()); ++i) {
const CutIsland& isl = m_result->cut_islands[i];
if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d))
return respect_disabled_contour ? !isl.disabled : true;
return i; // TODO: handle intersecting contours
}
return false;
return -1;
}
bool MeshClipper::has_valid_contour() const
@ -165,23 +166,6 @@ bool MeshClipper::has_valid_contour() const
return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); });
}
bool MeshClipper::has_disable_contour() const
{
return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return isl.disabled; });
}
void MeshClipper::pass_mouse_click(const Vec3d& point_in)
{
if (! m_result || m_result->cut_islands.empty())
return;
Vec3d point = m_result->trafo.inverse() * point_in;
Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y()));
for (CutIsland& isl : m_result->cut_islands) {
if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d))
isl.disabled = ! isl.disabled;
}
}
void MeshClipper::recalculate_triangles()
{

View File

@ -118,11 +118,10 @@ public:
void render_cut(const ColorRGBA& color);
void render_contour(const ColorRGBA& color);
void pass_mouse_click(const Vec3d& pt);
bool is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const;
// Returns index of the contour which was clicked, -1 otherwise.
int is_projection_inside_cut(const Vec3d& point) const;
bool has_valid_contour() const;
bool has_disable_contour() const;
int get_number_of_contours() const { return m_result ? m_result->cut_islands.size() : 0; }
private:
void recalculate_triangles();