Small refactoring of Cursors in TriangleSelector.

This commit is contained in:
Lukáš Hejl 2024-03-28 15:15:12 +01:00 committed by Lukas Matena
parent d23e114aed
commit f253822707
4 changed files with 85 additions and 85 deletions

View File

@ -247,21 +247,15 @@ void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&c
// have to pass it around.
m_cursor = std::move(cursor);
// In case user changed cursor size since last time, update triangle edge limit.
// It is necessary to compare the internal radius in m_cursor! radius is in
// world coords and does not change after scaling.
if (m_old_cursor_radius_sqr != m_cursor->radius_sqr) {
set_edge_limit(std::sqrt(m_cursor->radius_sqr) / 5.f);
m_old_cursor_radius_sqr = m_cursor->radius_sqr;
}
// In case user changed cursor parameters size since last time, update triangle edge limit.
set_edge_limit(m_cursor->get_edge_limit());
const float highlight_angle_limit = cos(Geometry::deg2rad(highlight_by_angle_deg));
Vec3f vec_down = (trafo_no_translate.inverse() * -Vec3d::UnitZ()).normalized().cast<float>();
// Now start with the facet the pointer points to and check all adjacent facets.
std::vector<int> facets_to_check;
std::vector<int> facets_to_check = m_cursor->get_facets_to_select(facet_start, m_vertices, m_triangles, m_orig_size_vertices, m_orig_size_indices);
facets_to_check.reserve(16);
facets_to_check.emplace_back(facet_start);
// Keep track of facets of the original mesh we already processed.
std::vector<bool> visited(m_orig_size_indices, false);
// Breadth-first search around the hit point. facets_to_check may grow significantly large.
@ -293,13 +287,13 @@ bool TriangleSelector::is_facet_clipped(int facet_idx, const ClippingPlane &clp)
}
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate,
const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg,
bool force_reselection)
const ClippingPlane &clp, float seed_fill_angle,
float highlight_by_angle_deg, ForceReselection force_reselection)
{
assert(facet_start < m_orig_size_indices);
// Recompute seed fill only if the cursor is pointing on facet unselected by seed fill or a clipping plane is active.
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active())
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && force_reselection == ForceReselection::NO && !clp.is_active())
return;
this->seed_fill_unselect_all_triangles();
@ -451,19 +445,20 @@ void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int ver
process_subtriangle(touching.second, Partition::Second);
}
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, const ClippingPlane &clp, bool propagate, bool force_reselection)
{
void TriangleSelector::bucket_fill_select_triangles(const Vec3f &hit, int facet_start, const ClippingPlane &clp,
BucketFillPropagate propagate,
ForceReselection force_reselection) {
int start_facet_idx = select_unsplit_triangle(hit, facet_start);
assert(start_facet_idx != -1);
// Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill or a clipping plane is active.
if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active()))
if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && force_reselection == ForceReselection::NO && !clp.is_active()))
return;
assert(!m_triangles[start_facet_idx].is_split());
TriangleStateType start_facet_state = m_triangles[start_facet_idx].get_state();
this->seed_fill_unselect_all_triangles();
if (!propagate) {
if (propagate == BucketFillPropagate::NO) {
m_triangles[start_facet_idx].select_by_seed_fill();
return;
}
@ -878,7 +873,7 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &nei
if (num_of_inside_vertices == 0
&& ! m_cursor->is_pointer_in_triangle(*tr, m_vertices)
&& ! m_cursor->is_edge_inside_cursor(*tr, m_vertices))
&& ! m_cursor->is_any_edge_inside_cursor(*tr, m_vertices))
return false;
if (num_of_inside_vertices == 3) {
@ -949,8 +944,8 @@ void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
// In case the object is non-uniformly scaled, transform the
// points to world coords.
if (! m_cursor->uniform_scaling) {
for (size_t i=0; i<pts.size(); ++i) {
if (!m_cursor->uniform_scaling) {
for (size_t i = 0; i < pts.size(); ++i) {
pts_transformed[i] = m_cursor->trafo * (*pts[i]);
pts[i] = &pts_transformed[i];
}
@ -1011,16 +1006,21 @@ int TriangleSelector::Cursor::vertices_inside(const Triangle &tr, const std::vec
return inside;
}
// Is any edge inside Sphere cursor?
bool TriangleSelector::Sphere::is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
{
inline std::array<Vec3f, 3> TriangleSelector::Cursor::transform_triangle(const Triangle &tr, const std::vector<Vertex> &vertices) const {
std::array<Vec3f, 3> pts;
for (int i = 0; i < 3; ++i) {
for (size_t i = 0; i < 3; ++i) {
pts[i] = vertices[tr.verts_idxs[i]].v;
if (!this->uniform_scaling)
pts[i] = this->trafo * pts[i];
}
return pts;
}
// Is any edge inside Sphere cursor?
bool TriangleSelector::Sphere::is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
{
const std::array<Vec3f, 3> pts = this->transform_triangle(tr, vertices);
for (int side = 0; side < 3; ++side) {
const Vec3f &edge_a = pts[side];
const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0];
@ -1031,16 +1031,10 @@ bool TriangleSelector::Sphere::is_edge_inside_cursor(const Triangle &tr, const s
}
// Is edge inside cursor?
bool TriangleSelector::Circle::is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
bool TriangleSelector::Circle::is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
{
std::array<Vec3f, 3> pts;
for (int i = 0; i < 3; ++i) {
pts[i] = vertices[tr.verts_idxs[i]].v;
if (!this->uniform_scaling)
pts[i] = this->trafo * pts[i];
}
const Vec3f &p = this->center;
const std::array<Vec3f, 3> pts = this->transform_triangle(tr, vertices);
const Vec3f &p = this->center;
for (int side = 0; side < 3; ++side) {
const Vec3f &a = pts[side];
const Vec3f &b = pts[side < 2 ? side + 1 : 0];
@ -1216,7 +1210,7 @@ void TriangleSelector::reset()
void TriangleSelector::set_edge_limit(float edge_limit)
{
m_edge_limit_sqr = std::pow(edge_limit, 2.f);
m_edge_limit_sqr = Slic3r::sqr(edge_limit);
}
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const TriangleStateType state) {
@ -1890,6 +1884,8 @@ TriangleSelector::Cursor::Cursor(const Vec3f &source_, float radius_world, const
radius_sqr = Slic3r::sqr(radius_world);
trafo_normal = trafo.linear().inverse().transpose();
}
m_edge_limit = std::sqrt(radius_sqr) / 5.f;
}
TriangleSelector::SinglePointCursor::SinglePointCursor(const Vec3f& center_, const Vec3f& source_, float radius_world, const Transform3d& trafo_, const ClippingPlane &clipping_plane_)
@ -2048,15 +2044,9 @@ bool line_plane_intersection(const Vec3f &line_a, const Vec3f &line_b, const Vec
return false;
}
bool TriangleSelector::Capsule3D::is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
bool TriangleSelector::Capsule3D::is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
{
std::array<Vec3f, 3> pts;
for (int i = 0; i < 3; ++i) {
pts[i] = vertices[tr.verts_idxs[i]].v;
if (!this->uniform_scaling)
pts[i] = this->trafo * pts[i];
}
const std::array<Vec3f, 3> pts = this->transform_triangle(tr, vertices);
for (int side = 0; side < 3; ++side) {
const Vec3f &edge_a = pts[side];
const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0];
@ -2068,15 +2058,8 @@ bool TriangleSelector::Capsule3D::is_edge_inside_cursor(const Triangle &tr, cons
}
// Is edge inside cursor?
bool TriangleSelector::Capsule2D::is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
bool TriangleSelector::Capsule2D::is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
{
std::array<Vec3f, 3> pts;
for (int i = 0; i < 3; ++i) {
pts[i] = vertices[tr.verts_idxs[i]].v;
if (!this->uniform_scaling)
pts[i] = this->trafo * pts[i];
}
const Vec3f centers_diff = this->second_center - this->first_center;
// Vector in the direction of line |AD| of the rectangle that intersects the circle with the center in first_center.
const Vec3f rectangle_da_dir = centers_diff.cross(this->dir);
@ -2095,6 +2078,7 @@ bool TriangleSelector::Capsule2D::is_edge_inside_cursor(const Triangle &tr, cons
return false;
};
const std::array<Vec3f, 3> pts = this->transform_triangle(tr, vertices);
for (int side = 0; side < 3; ++side) {
const Vec3f &edge_a = pts[side];
const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0];

View File

@ -67,12 +67,22 @@ protected:
struct Vertex;
public:
enum CursorType {
enum class CursorType {
CIRCLE,
SPHERE,
POINTER
};
enum class ForceReselection {
NO,
YES
};
enum class BucketFillPropagate {
NO,
YES
};
struct ClippingPlane
{
Vec3f normal;
@ -91,14 +101,21 @@ public:
Cursor() = delete;
virtual ~Cursor() = default;
float get_edge_limit() { return m_edge_limit; };
bool is_pointer_in_triangle(const Triangle &tr, const std::vector<Vertex> &vertices) const;
std::array<Vec3f, 3> transform_triangle(const Triangle &tr, const std::vector<Vertex> &vertices) const;
virtual bool is_mesh_point_inside(const Vec3f &point) const = 0;
virtual bool is_pointer_in_triangle(const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) const = 0;
virtual int vertices_inside(const Triangle &tr, const std::vector<Vertex> &vertices) const;
virtual bool is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const = 0;
virtual bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const = 0;
virtual bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const = 0;
virtual std::vector<int> get_facets_to_select(int facet_idx, const std::vector<Vertex> &vertices, const std::vector<Triangle> &triangles, int orig_size_vertices, int orig_size_indices) const {
return { facet_idx };
};
static bool is_facet_visible(const Cursor &cursor, int facet_idx, const std::vector<Vec3f> &face_normals);
protected:
@ -115,6 +132,8 @@ public:
ClippingPlane clipping_plane; // Clipping plane to limit painting to not clipped facets only
float m_edge_limit;
friend TriangleSelector;
};
@ -174,7 +193,7 @@ public:
~Sphere() override = default;
bool is_mesh_point_inside(const Vec3f &point) const override;
bool is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override { return true; }
};
@ -187,9 +206,8 @@ public:
~Circle() override = default;
bool is_mesh_point_inside(const Vec3f &point) const override;
bool is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override
{
bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override {
return TriangleSelector::Cursor::is_facet_visible(*this, facet_idx, face_normals);
}
};
@ -204,7 +222,7 @@ public:
~Capsule3D() override = default;
bool is_mesh_point_inside(const Vec3f &point) const override;
bool is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override { return true; }
};
@ -218,9 +236,8 @@ public:
~Capsule2D() override = default;
bool is_mesh_point_inside(const Vec3f &point) const override;
bool is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override
{
bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const override;
bool is_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override {
return TriangleSelector::Cursor::is_facet_visible(*this, facet_idx, face_normals);
}
};
@ -298,19 +315,19 @@ public:
bool triangle_splitting, // If triangles will be split base on the cursor or not
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
void seed_fill_select_triangles(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
void seed_fill_select_triangles(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
ForceReselection force_reselection = ForceReselection::NO); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
BucketFillPropagate propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
ForceReselection force_reselection = ForceReselection::NO); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
bool has_facets(TriangleStateType state) const;
static bool has_facets(const TriangleSplittingData &data, TriangleStateType test_state);
@ -450,8 +467,6 @@ protected:
int m_orig_size_indices = 0;
std::unique_ptr<Cursor> m_cursor;
// Zero indicates an uninitialized state.
float m_old_cursor_radius_sqr = 0;
// Private functions:
private:

View File

@ -139,9 +139,9 @@ void GLGizmoPainterBase::render_cursor()
return;
if (m_tool_type == ToolType::BRUSH) {
if (m_cursor_type == TriangleSelector::SPHERE)
if (m_cursor_type == TriangleSelector::CursorType::SPHERE)
render_cursor_sphere(trafo_matrices[m_rr.mesh_id]);
else if (m_cursor_type == TriangleSelector::CIRCLE)
else if (m_cursor_type == TriangleSelector::CursorType::CIRCLE)
render_cursor_circle();
}
}
@ -480,7 +480,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset();
const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle,
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, TriangleSelector::ForceReselection::YES);
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
m_seed_fill_last_mesh_id = m_rr.mesh_id;
}
@ -561,13 +561,14 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const Vec3f mesh_hit = projected_mouse_position.mesh_hit;
const int facet_idx = int(projected_mouse_position.facet_idx);
m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state);
if (m_tool_type == ToolType::SMART_FILL)
if (m_tool_type == ToolType::SMART_FILL) {
m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle,
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, false, true);
else if (m_tool_type == ToolType::BUCKET_FILL)
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, true, true);
(m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f), TriangleSelector::ForceReselection::YES);
} else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) {
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, TriangleSelector::BucketFillPropagate::NO, TriangleSelector::ForceReselection::YES);
} else if (m_tool_type == ToolType::BUCKET_FILL) {
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, TriangleSelector::BucketFillPropagate::YES, TriangleSelector::ForceReselection::YES);
}
m_seed_fill_last_mesh_id = -1;
}
@ -649,9 +650,9 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false);
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, TriangleSelector::BucketFillPropagate::NO);
else if (m_tool_type == ToolType::BUCKET_FILL)
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true);
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, TriangleSelector::BucketFillPropagate::YES);
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
m_seed_fill_last_mesh_id = m_rr.mesh_id;
return true;

View File

@ -132,7 +132,7 @@ protected:
// For each model-part volume, store status and division of the triangles.
std::vector<std::unique_ptr<TriangleSelectorGUI>> m_triangle_selectors;
TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE;
TriangleSelector::CursorType m_cursor_type = TriangleSelector::CursorType::SPHERE;
enum class ToolType {
BRUSH,