Merge branch 'lh_cursors'
This commit is contained in:
commit
0875dc73c7
@ -9,6 +9,112 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Check if the line is whole inside the sphere, or it is partially inside (intersecting) the sphere.
|
||||||
|
// Inspired by Christer Ericson's Real-Time Collision Detection, pp. 177-179.
|
||||||
|
static bool test_line_inside_sphere(const Vec3f &line_a, const Vec3f &line_b, const Vec3f &sphere_p, const float sphere_radius)
|
||||||
|
{
|
||||||
|
const float sphere_radius_sqr = Slic3r::sqr(sphere_radius);
|
||||||
|
const Vec3f line_dir = line_b - line_a; // n
|
||||||
|
const Vec3f origins_diff = line_a - sphere_p; // m
|
||||||
|
|
||||||
|
const float m_dot_m = origins_diff.dot(origins_diff);
|
||||||
|
// Check if any of the end-points of the line is inside the sphere.
|
||||||
|
if (m_dot_m <= sphere_radius_sqr || (line_b - sphere_p).squaredNorm() <= sphere_radius_sqr)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if the infinite line is going through the sphere.
|
||||||
|
const float n_dot_n = line_dir.dot(line_dir);
|
||||||
|
const float m_dot_n = origins_diff.dot(line_dir);
|
||||||
|
|
||||||
|
const float eq_a = n_dot_n;
|
||||||
|
const float eq_b = m_dot_n;
|
||||||
|
const float eq_c = m_dot_m - sphere_radius_sqr;
|
||||||
|
|
||||||
|
const float discr = eq_b * eq_b - eq_a * eq_c;
|
||||||
|
// A negative discriminant corresponds to the infinite line infinite not going through the sphere.
|
||||||
|
if (discr < 0.f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the finite line is going through the sphere.
|
||||||
|
const float discr_sqrt = std::sqrt(discr);
|
||||||
|
const float t1 = (-eq_b - discr_sqrt) / eq_a;
|
||||||
|
if (0.f <= t1 && t1 <= 1.f)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const float t2 = (-eq_b + discr_sqrt) / eq_a;
|
||||||
|
if (0.f <= t2 && t2 <= 1.f && discr_sqrt > 0.f)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the line is whole inside the finite cylinder, or it is partially inside (intersecting) the finite cylinder.
|
||||||
|
// Inspired by Christer Ericson's Real-Time Collision Detection, pp. 194-198.
|
||||||
|
static bool test_line_inside_cylinder(const Vec3f &line_a, const Vec3f &line_b, const Vec3f &cylinder_P, const Vec3f &cylinder_Q, const float cylinder_radius)
|
||||||
|
{
|
||||||
|
assert(cylinder_P != cylinder_Q);
|
||||||
|
const Vec3f cylinder_dir = cylinder_Q - cylinder_P; // d
|
||||||
|
auto is_point_inside_finite_cylinder = [&cylinder_P, &cylinder_Q, &cylinder_radius, &cylinder_dir](const Vec3f &pt) {
|
||||||
|
const Vec3f first_center_diff = cylinder_P - pt;
|
||||||
|
const Vec3f second_center_diff = cylinder_Q - pt;
|
||||||
|
// First, check if the point pt is laying between planes defined by cylinder_p and cylinder_q.
|
||||||
|
// Then check if it is inside the cylinder between cylinder_p and cylinder_q.
|
||||||
|
return first_center_diff.dot(cylinder_dir) <= 0 && second_center_diff.dot(cylinder_dir) >= 0 &&
|
||||||
|
(first_center_diff.cross(cylinder_dir).norm() / cylinder_dir.norm()) <= cylinder_radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if any of the end-points of the line is inside the cylinder.
|
||||||
|
if (is_point_inside_finite_cylinder(line_a) || is_point_inside_finite_cylinder(line_b))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if the line is going through the cylinder.
|
||||||
|
const Vec3f origins_diff = line_a - cylinder_P; // m
|
||||||
|
const Vec3f line_dir = line_b - line_a; // n
|
||||||
|
|
||||||
|
const float m_dot_d = origins_diff.dot(cylinder_dir);
|
||||||
|
const float n_dot_d = line_dir.dot(cylinder_dir);
|
||||||
|
const float d_dot_d = cylinder_dir.dot(cylinder_dir);
|
||||||
|
|
||||||
|
const float n_dot_n = line_dir.dot(line_dir);
|
||||||
|
const float m_dot_n = origins_diff.dot(line_dir);
|
||||||
|
const float m_dot_m = origins_diff.dot(origins_diff);
|
||||||
|
|
||||||
|
const float eq_a = d_dot_d * n_dot_n - n_dot_d * n_dot_d;
|
||||||
|
const float eq_b = d_dot_d * m_dot_n - n_dot_d * m_dot_d;
|
||||||
|
const float eq_c = d_dot_d * (m_dot_m - Slic3r::sqr(cylinder_radius)) - m_dot_d * m_dot_d;
|
||||||
|
|
||||||
|
const float discr = eq_b * eq_b - eq_a * eq_c;
|
||||||
|
// A negative discriminant corresponds to the infinite line not going through the infinite cylinder.
|
||||||
|
if (discr < 0.0f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the finite line is going through the finite cylinder.
|
||||||
|
const float discr_sqrt = std::sqrt(discr);
|
||||||
|
const float t1 = (-eq_b - discr_sqrt) / eq_a;
|
||||||
|
if (0.f <= t1 && t1 <= 1.f)
|
||||||
|
if (const float cylinder_endcap_t1 = m_dot_d + t1 * n_dot_d; 0.f <= cylinder_endcap_t1 && cylinder_endcap_t1 <= d_dot_d)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const float t2 = (-eq_b + discr_sqrt) / eq_a;
|
||||||
|
if (0.f <= t2 && t2 <= 1.f)
|
||||||
|
if (const float cylinder_endcap_t2 = (m_dot_d + t2 * n_dot_d); 0.f <= cylinder_endcap_t2 && cylinder_endcap_t2 <= d_dot_d)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the line is whole inside the capsule, or it is partially inside (intersecting) the capsule.
|
||||||
|
static bool test_line_inside_capsule(const Vec3f &line_a, const Vec3f &line_b, const Vec3f &capsule_p, const Vec3f &capsule_q, const float capsule_radius) {
|
||||||
|
assert(capsule_p != capsule_q);
|
||||||
|
|
||||||
|
// Check if the line intersect any of the spheres forming the capsule.
|
||||||
|
if (test_line_inside_sphere(line_a, line_b, capsule_p, capsule_radius) || test_line_inside_sphere(line_a, line_b, capsule_q, capsule_radius))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if the line intersects the cylinder between the centers of the spheres.
|
||||||
|
return test_line_inside_cylinder(line_a, line_b, capsule_p, capsule_q, capsule_radius);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
|
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
|
||||||
{
|
{
|
||||||
@ -124,24 +230,20 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
|
|||||||
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg)
|
||||||
const Vec3f& source, float radius,
|
|
||||||
CursorType cursor_type, EnforcerBlockerType new_state,
|
|
||||||
const Transform3d& trafo, const Transform3d& trafo_no_translate,
|
|
||||||
bool triangle_splitting, const ClippingPlane &clp, float highlight_by_angle_deg)
|
|
||||||
{
|
{
|
||||||
assert(facet_start < m_orig_size_indices);
|
assert(facet_start < m_orig_size_indices);
|
||||||
|
|
||||||
// Save current cursor center, squared radius and camera direction, so we don't
|
// Save current cursor center, squared radius and camera direction, so we don't
|
||||||
// have to pass it around.
|
// have to pass it around.
|
||||||
m_cursor = Cursor(hit, source, radius, cursor_type, trafo, clp);
|
m_cursor = std::move(cursor);
|
||||||
|
|
||||||
// In case user changed cursor size since last time, update triangle edge limit.
|
// 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
|
// It is necessary to compare the internal radius in m_cursor! radius is in
|
||||||
// world coords and does not change after scaling.
|
// world coords and does not change after scaling.
|
||||||
if (m_old_cursor_radius_sqr != m_cursor.radius_sqr) {
|
if (m_old_cursor_radius_sqr != m_cursor->radius_sqr) {
|
||||||
set_edge_limit(std::sqrt(m_cursor.radius_sqr) / 5.f);
|
set_edge_limit(std::sqrt(m_cursor->radius_sqr) / 5.f);
|
||||||
m_old_cursor_radius_sqr = m_cursor.radius_sqr;
|
m_old_cursor_radius_sqr = m_cursor->radius_sqr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float highlight_angle_limit = cos(Geometry::deg2rad(highlight_by_angle_deg));
|
const float highlight_angle_limit = cos(Geometry::deg2rad(highlight_by_angle_deg));
|
||||||
@ -163,7 +265,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|||||||
if (select_triangle(facet, new_state, triangle_splitting)) {
|
if (select_triangle(facet, new_state, triangle_splitting)) {
|
||||||
// add neighboring facets to list to be processed later
|
// add neighboring facets to list to be processed later
|
||||||
for (int neighbor_idx : m_neighbors[facet])
|
for (int neighbor_idx : m_neighbors[facet])
|
||||||
if (neighbor_idx >= 0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
|
if (neighbor_idx >= 0 && m_cursor->is_facet_visible(neighbor_idx, m_face_normals))
|
||||||
facets_to_check.push_back(neighbor_idx);
|
facets_to_check.push_back(neighbor_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -788,11 +890,11 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &nei
|
|||||||
|
|
||||||
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
||||||
|
|
||||||
int num_of_inside_vertices = vertices_inside(facet_idx);
|
int num_of_inside_vertices = m_cursor->vertices_inside(*tr, m_vertices);
|
||||||
|
|
||||||
if (num_of_inside_vertices == 0
|
if (num_of_inside_vertices == 0
|
||||||
&& ! is_pointer_in_triangle(facet_idx)
|
&& ! m_cursor->is_pointer_in_triangle(*tr, m_vertices)
|
||||||
&& ! is_edge_inside_cursor(facet_idx))
|
&& ! m_cursor->is_edge_inside_cursor(*tr, m_vertices))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (num_of_inside_vertices == 3) {
|
if (num_of_inside_vertices == 3) {
|
||||||
@ -840,7 +942,7 @@ void TriangleSelector::set_facet(int facet_idx, EnforcerBlockerType state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called by select_patch()->select_triangle()...select_triangle()
|
// called by select_patch()->select_triangle()...select_triangle()
|
||||||
// to decide which sides of the traingle to split and to actually split it calling set_division() and perform_split().
|
// to decide which sides of the triangle to split and to actually split it calling set_division() and perform_split().
|
||||||
void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
|
void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
|
||||||
{
|
{
|
||||||
if (m_triangles[facet_idx].is_split()) {
|
if (m_triangles[facet_idx].is_split()) {
|
||||||
@ -864,9 +966,9 @@ void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
|
|||||||
|
|
||||||
// In case the object is non-uniformly scaled, transform the
|
// In case the object is non-uniformly scaled, transform the
|
||||||
// points to world coords.
|
// points to world coords.
|
||||||
if (! m_cursor.uniform_scaling) {
|
if (! m_cursor->uniform_scaling) {
|
||||||
for (size_t i=0; i<pts.size(); ++i) {
|
for (size_t i=0; i<pts.size(); ++i) {
|
||||||
pts_transformed[i] = m_cursor.trafo * (*pts[i]);
|
pts_transformed[i] = m_cursor->trafo * (*pts[i]);
|
||||||
pts[i] = &pts_transformed[i];
|
pts[i] = &pts_transformed[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -897,71 +999,80 @@ void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
|
|||||||
perform_split(facet_idx, neighbors, old_type);
|
perform_split(facet_idx, neighbors, old_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Is pointer in a triangle?
|
// Is pointer in a triangle?
|
||||||
bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
|
bool TriangleSelector::Cursor::is_pointer_in_triangle(const Triangle &tr, const std::vector<Vertex> &vertices) const {
|
||||||
{
|
const Vec3f& p1 = vertices[tr.verts_idxs[0]].v;
|
||||||
const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]].v;
|
const Vec3f& p2 = vertices[tr.verts_idxs[1]].v;
|
||||||
const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v;
|
const Vec3f& p3 = vertices[tr.verts_idxs[2]].v;
|
||||||
const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v;
|
return this->is_pointer_in_triangle(p1, p2, p3);
|
||||||
return m_cursor.is_pointer_in_triangle(p1, p2, p3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Determine whether this facet is potentially visible (still can be obscured).
|
// Determine whether this facet is potentially visible (still can be obscured).
|
||||||
bool TriangleSelector::faces_camera(int facet) const
|
bool TriangleSelector::Cursor::is_facet_visible(const Cursor &cursor, int facet_idx, const std::vector<Vec3f> &face_normals)
|
||||||
{
|
{
|
||||||
assert(facet < m_orig_size_indices);
|
assert(facet_idx < int(face_normals.size()));
|
||||||
Vec3f n = m_face_normals[facet];
|
Vec3f n = face_normals[facet_idx];
|
||||||
if (! m_cursor.uniform_scaling)
|
if (!cursor.uniform_scaling)
|
||||||
n = m_cursor.trafo_normal * n;
|
n = cursor.trafo_normal * n;
|
||||||
return n.dot(m_cursor.dir) < 0.;
|
return n.dot(cursor.dir) < 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// How many vertices of a triangle are inside the circle?
|
// How many vertices of a triangle are inside the circle?
|
||||||
int TriangleSelector::vertices_inside(int facet_idx) const
|
int TriangleSelector::Cursor::vertices_inside(const Triangle &tr, const std::vector<Vertex> &vertices) const
|
||||||
{
|
{
|
||||||
int inside = 0;
|
int inside = 0;
|
||||||
for (size_t i=0; i<3; ++i) {
|
for (size_t i = 0; i < 3; ++i)
|
||||||
if (m_cursor.is_mesh_point_inside(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v))
|
if (this->is_mesh_point_inside(vertices[tr.verts_idxs[i]].v))
|
||||||
++inside;
|
++inside;
|
||||||
}
|
|
||||||
return inside;
|
return inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is any edge inside Sphere cursor?
|
||||||
// Is edge inside cursor?
|
bool TriangleSelector::Sphere::is_edge_inside_cursor(const Triangle &tr, const std::vector<Vertex> &vertices) const
|
||||||
bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const
|
|
||||||
{
|
{
|
||||||
std::array<Vec3f, 3> pts;
|
std::array<Vec3f, 3> pts;
|
||||||
for (int i=0; i<3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v;
|
pts[i] = vertices[tr.verts_idxs[i]].v;
|
||||||
if (! m_cursor.uniform_scaling)
|
if (!this->uniform_scaling)
|
||||||
pts[i] = m_cursor.trafo * pts[i];
|
pts[i] = this->trafo * pts[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vec3f& p = m_cursor.center;
|
|
||||||
|
|
||||||
for (int side = 0; side < 3; ++side) {
|
for (int side = 0; side < 3; ++side) {
|
||||||
const Vec3f& a = pts[side];
|
const Vec3f &edge_a = pts[side];
|
||||||
const Vec3f& b = pts[side<2 ? side+1 : 0];
|
const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0];
|
||||||
Vec3f s = (b-a).normalized();
|
if (test_line_inside_sphere(edge_a, edge_b, this->center, this->radius))
|
||||||
float t = (p-a).dot(s);
|
|
||||||
Vec3f vector = a+t*s - p;
|
|
||||||
|
|
||||||
// vector is 3D vector from center to the intersection. What we want to
|
|
||||||
// measure is length of its projection onto plane perpendicular to dir.
|
|
||||||
float dist_sqr = vector.squaredNorm() - std::pow(vector.dot(m_cursor.dir), 2.f);
|
|
||||||
if (dist_sqr < m_cursor.radius_sqr && t>=0.f && t<=(b-a).norm())
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is edge inside cursor?
|
||||||
|
bool TriangleSelector::Circle::is_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;
|
||||||
|
for (int side = 0; side < 3; ++side) {
|
||||||
|
const Vec3f &a = pts[side];
|
||||||
|
const Vec3f &b = pts[side < 2 ? side + 1 : 0];
|
||||||
|
Vec3f s = (b - a).normalized();
|
||||||
|
float t = (p - a).dot(s);
|
||||||
|
Vec3f vector = a + t * s - p;
|
||||||
|
|
||||||
|
// vector is 3D vector from center to the intersection. What we want to
|
||||||
|
// measure is length of its projection onto plane perpendicular to dir.
|
||||||
|
float dist_sqr = vector.squaredNorm() - std::pow(vector.dot(this->dir), 2.f);
|
||||||
|
if (dist_sqr < this->radius_sqr && t >= 0.f && t <= (b - a).norm())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively remove all subtriangles.
|
// Recursively remove all subtriangles.
|
||||||
void TriangleSelector::undivide_triangle(int facet_idx)
|
void TriangleSelector::undivide_triangle(int facet_idx)
|
||||||
@ -1002,7 +1113,6 @@ void TriangleSelector::undivide_triangle(int facet_idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TriangleSelector::remove_useless_children(int facet_idx)
|
void TriangleSelector::remove_useless_children(int facet_idx)
|
||||||
{
|
{
|
||||||
// Check that all children are leafs of the same type. If not, try to
|
// Check that all children are leafs of the same type. If not, try to
|
||||||
@ -1041,8 +1151,6 @@ void TriangleSelector::remove_useless_children(int facet_idx)
|
|||||||
tr.set_state(first_child_type);
|
tr.set_state(first_child_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void TriangleSelector::garbage_collect()
|
void TriangleSelector::garbage_collect()
|
||||||
{
|
{
|
||||||
// First make a map from old to new triangle indices.
|
// First make a map from old to new triangle indices.
|
||||||
@ -1103,7 +1211,6 @@ TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
|
|||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TriangleSelector::reset()
|
void TriangleSelector::reset()
|
||||||
{
|
{
|
||||||
m_vertices.clear();
|
m_vertices.clear();
|
||||||
@ -1124,17 +1231,11 @@ void TriangleSelector::reset()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void TriangleSelector::set_edge_limit(float edge_limit)
|
void TriangleSelector::set_edge_limit(float edge_limit)
|
||||||
{
|
{
|
||||||
m_edge_limit_sqr = std::pow(edge_limit, 2.f);
|
m_edge_limit_sqr = std::pow(edge_limit, 2.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state)
|
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state)
|
||||||
{
|
{
|
||||||
for (int i : {a, b, c}) {
|
for (int i : {a, b, c}) {
|
||||||
@ -1693,54 +1794,132 @@ void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_stat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TriangleSelector::Cursor::Cursor(
|
TriangleSelector::Cursor::Cursor(const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_)
|
||||||
const Vec3f& center_, const Vec3f& source_, float radius_world,
|
: source{source_}, trafo{trafo_.cast<float>()}, clipping_plane{clipping_plane_}
|
||||||
CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_)
|
|
||||||
: center{center_},
|
|
||||||
source{source_},
|
|
||||||
type{type_},
|
|
||||||
trafo{trafo_.cast<float>()},
|
|
||||||
clipping_plane(clipping_plane_)
|
|
||||||
{
|
{
|
||||||
Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor();
|
Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor();
|
||||||
if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) {
|
if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) {
|
||||||
radius_sqr = float(std::pow(radius_world / sf(0), 2));
|
radius = float(radius_world / sf(0));
|
||||||
|
radius_sqr = float(Slic3r::sqr(radius_world / sf(0)));
|
||||||
uniform_scaling = true;
|
uniform_scaling = true;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// In case that the transformation is non-uniform, all checks whether
|
// In case that the transformation is non-uniform, all checks whether
|
||||||
// something is inside the cursor should be done in world coords.
|
// something is inside the cursor should be done in world coords.
|
||||||
// First transform center, source and dir in world coords and remember
|
// First transform source in world coords and remember that we did this.
|
||||||
// that we did this.
|
source = trafo * source;
|
||||||
center = trafo * center;
|
|
||||||
source = trafo * source;
|
|
||||||
uniform_scaling = false;
|
uniform_scaling = false;
|
||||||
radius_sqr = radius_world * radius_world;
|
radius = radius_world;
|
||||||
trafo_normal = trafo.linear().inverse().transpose();
|
radius_sqr = Slic3r::sqr(radius_world);
|
||||||
|
trafo_normal = trafo.linear().inverse().transpose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TriangleSelector::SinglePointCursor::SinglePointCursor(const Vec3f& center_, const Vec3f& source_, float radius_world, const Transform3d& trafo_, const ClippingPlane &clipping_plane_)
|
||||||
|
: center{center_}, Cursor(source_, radius_world, trafo_, clipping_plane_)
|
||||||
|
{
|
||||||
|
// In case that the transformation is non-uniform, all checks whether
|
||||||
|
// something is inside the cursor should be done in world coords.
|
||||||
|
// Because of the center is transformed.
|
||||||
|
if (!uniform_scaling)
|
||||||
|
center = trafo * center;
|
||||||
|
|
||||||
// Calculate dir, in whatever coords is appropriate.
|
// Calculate dir, in whatever coords is appropriate.
|
||||||
dir = (center - source).normalized();
|
dir = (center - source).normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is a point (in mesh coords) inside a cursor?
|
TriangleSelector::DoublePointCursor::DoublePointCursor(const Vec3f &first_center_, const Vec3f &second_center_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_)
|
||||||
bool TriangleSelector::Cursor::is_mesh_point_inside(const Vec3f &point) const
|
: first_center{first_center_}, second_center{second_center_}, Cursor(source_, radius_world, trafo_, clipping_plane_)
|
||||||
|
{
|
||||||
|
if (!uniform_scaling) {
|
||||||
|
first_center = trafo * first_center_;
|
||||||
|
second_center = trafo * second_center_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate dir, in whatever coords is appropriate.
|
||||||
|
dir = (first_center - source).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if clipping plane is not active or if the point not clipped by clipping plane.
|
||||||
|
inline static bool is_mesh_point_not_clipped(const Vec3f &point, const TriangleSelector::ClippingPlane &clipping_plane)
|
||||||
|
{
|
||||||
|
return !clipping_plane.is_active() || !clipping_plane.is_mesh_point_clipped(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a point (in mesh coords) inside a Sphere cursor?
|
||||||
|
bool TriangleSelector::Sphere::is_mesh_point_inside(const Vec3f &point) const
|
||||||
|
{
|
||||||
|
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
||||||
|
if ((center - transformed_point).squaredNorm() < radius_sqr)
|
||||||
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a point (in mesh coords) inside a Circle cursor?
|
||||||
|
bool TriangleSelector::Circle::is_mesh_point_inside(const Vec3f &point) const
|
||||||
{
|
{
|
||||||
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
||||||
const Vec3f diff = center - transformed_point;
|
const Vec3f diff = center - transformed_point;
|
||||||
const bool is_point_inside = (type == CIRCLE ? (diff - diff.dot(dir) * dir).squaredNorm() : diff.squaredNorm()) < radius_sqr;
|
|
||||||
|
|
||||||
if (is_point_inside && clipping_plane.is_active())
|
if ((diff - diff.dot(dir) * dir).squaredNorm() < radius_sqr)
|
||||||
return !clipping_plane.is_mesh_point_clipped(point);
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
|
||||||
return is_point_inside;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a point (in mesh coords) inside a Capsule3D cursor?
|
||||||
|
bool TriangleSelector::Capsule3D::is_mesh_point_inside(const Vec3f &point) const
|
||||||
|
{
|
||||||
|
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
||||||
|
const Vec3f first_center_diff = this->first_center - transformed_point;
|
||||||
|
const Vec3f second_center_diff = this->second_center - transformed_point;
|
||||||
|
if (first_center_diff.squaredNorm() < this->radius_sqr || second_center_diff.squaredNorm() < this->radius_sqr)
|
||||||
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
|
||||||
|
// First, check if the point pt is laying between planes defined by first_center and second_center.
|
||||||
|
// Then check if it is inside the cylinder between first_center and second_center.
|
||||||
|
const Vec3f centers_diff = this->second_center - this->first_center;
|
||||||
|
if (first_center_diff.dot(centers_diff) <= 0.f && second_center_diff.dot(centers_diff) >= 0.f && (first_center_diff.cross(centers_diff).norm() / centers_diff.norm()) <= this->radius)
|
||||||
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a point (in mesh coords) inside a Capsule2D cursor?
|
||||||
|
bool TriangleSelector::Capsule2D::is_mesh_point_inside(const Vec3f &point) const
|
||||||
|
{
|
||||||
|
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
||||||
|
const Vec3f first_center_diff = this->first_center - transformed_point;
|
||||||
|
const Vec3f first_center_diff_projected = first_center_diff - first_center_diff.dot(this->dir) * this->dir;
|
||||||
|
if (first_center_diff_projected.squaredNorm() < this->radius_sqr)
|
||||||
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
|
||||||
|
const Vec3f second_center_diff = this->second_center - transformed_point;
|
||||||
|
const Vec3f second_center_diff_projected = second_center_diff - second_center_diff.dot(this->dir) * this->dir;
|
||||||
|
if (second_center_diff_projected.squaredNorm() < this->radius_sqr)
|
||||||
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
|
||||||
|
const Vec3f centers_diff = this->second_center - this->first_center;
|
||||||
|
const Vec3f centers_diff_projected = centers_diff - centers_diff.dot(this->dir) * this->dir;
|
||||||
|
|
||||||
|
// First, check if the point is laying between first_center and second_center.
|
||||||
|
if (first_center_diff_projected.dot(centers_diff_projected) <= 0.f && second_center_diff_projected.dot(centers_diff_projected) >= 0.f) {
|
||||||
|
// 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);
|
||||||
|
// Vector pointing from first_center to the point 'A' of the rectangle.
|
||||||
|
const Vec3f first_center_rectangle_a_diff = rectangle_da_dir.normalized() * this->radius;
|
||||||
|
const Vec3f rectangle_a = this->first_center - first_center_rectangle_a_diff;
|
||||||
|
const Vec3f rectangle_d = this->first_center + first_center_rectangle_a_diff;
|
||||||
|
// Now check if the point is laying inside the rectangle between circles with centers in first_center and second_center.
|
||||||
|
if ((rectangle_a - transformed_point).dot(rectangle_da_dir) <= 0.f && (rectangle_d - transformed_point).dot(rectangle_da_dir) >= 0.f)
|
||||||
|
return is_mesh_point_not_clipped(point, clipping_plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// p1, p2, p3 are in mesh coords!
|
// p1, p2, p3 are in mesh coords!
|
||||||
bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_,
|
static bool is_circle_pointer_inside_triangle(const Vec3f &p1_, const Vec3f &p2_, const Vec3f &p3_, const Vec3f ¢er, const Vec3f &dir, const bool uniform_scaling, const Transform3f &trafo) {
|
||||||
const Vec3f& p2_,
|
|
||||||
const Vec3f& p3_) const
|
|
||||||
{
|
|
||||||
const Vec3f& q1 = center + dir;
|
const Vec3f& q1 = center + dir;
|
||||||
const Vec3f& q2 = center - dir;
|
const Vec3f& q2 = center - dir;
|
||||||
|
|
||||||
@ -1761,4 +1940,108 @@ bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_,
|
|||||||
return signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos;
|
return signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// p1, p2, p3 are in mesh coords!
|
||||||
|
bool TriangleSelector::SinglePointCursor::is_pointer_in_triangle(const Vec3f &p1_, const Vec3f &p2_, const Vec3f &p3_) const
|
||||||
|
{
|
||||||
|
return is_circle_pointer_inside_triangle(p1_, p2_, p3_, center, dir, uniform_scaling, trafo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// p1, p2, p3 are in mesh coords!
|
||||||
|
bool TriangleSelector::DoublePointCursor::is_pointer_in_triangle(const Vec3f &p1_, const Vec3f &p2_, const Vec3f &p3_) const
|
||||||
|
{
|
||||||
|
return is_circle_pointer_inside_triangle(p1_, p2_, p3_, first_center, dir, uniform_scaling, trafo) ||
|
||||||
|
is_circle_pointer_inside_triangle(p1_, p2_, p3_, second_center, dir, uniform_scaling, trafo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool line_plane_intersection(const Vec3f &line_a, const Vec3f &line_b, const Vec3f &plane_origin, const Vec3f &plane_normal, Vec3f &out_intersection)
|
||||||
|
{
|
||||||
|
Vec3f line_dir = line_b - line_a;
|
||||||
|
float t_denominator = plane_normal.dot(line_dir);
|
||||||
|
if (t_denominator == 0.f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Compute 'd' in plane equation by using some point (origin) on the plane
|
||||||
|
float plane_d = plane_normal.dot(plane_origin);
|
||||||
|
if (float t = (plane_d - plane_normal.dot(line_a)) / t_denominator; t >= 0.f && t <= 1.f) {
|
||||||
|
out_intersection = line_a + t * line_dir;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TriangleSelector::Capsule3D::is_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];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int side = 0; side < 3; ++side) {
|
||||||
|
const Vec3f &edge_a = pts[side];
|
||||||
|
const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0];
|
||||||
|
if (test_line_inside_capsule(edge_a, edge_b, this->first_center, this->second_center, this->radius))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is edge inside cursor?
|
||||||
|
bool TriangleSelector::Capsule2D::is_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);
|
||||||
|
// Vector pointing from first_center to the point 'A' of the rectangle.
|
||||||
|
const Vec3f first_center_rectangle_a_diff = rectangle_da_dir.normalized() * this->radius;
|
||||||
|
const Vec3f rectangle_a = this->first_center - first_center_rectangle_a_diff;
|
||||||
|
const Vec3f rectangle_d = this->first_center + first_center_rectangle_a_diff;
|
||||||
|
|
||||||
|
auto edge_inside_rectangle = [&self = std::as_const(*this), ¢ers_diff](const Vec3f &edge_a, const Vec3f &edge_b, const Vec3f &plane_origin, const Vec3f &plane_normal) -> bool {
|
||||||
|
Vec3f intersection(-1.f, -1.f, -1.f);
|
||||||
|
if (line_plane_intersection(edge_a, edge_b, plane_origin, plane_normal, intersection)) {
|
||||||
|
// Now check if the intersection point is inside the rectangle. That means it is between 'first_center' and 'second_center', resp. between 'A' and 'B'.
|
||||||
|
if (self.first_center.dot(centers_diff) <= intersection.dot(centers_diff) && intersection.dot(centers_diff) <= self.second_center.dot(centers_diff))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int side = 0; side < 3; ++side) {
|
||||||
|
const Vec3f &edge_a = pts[side];
|
||||||
|
const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0];
|
||||||
|
const Vec3f edge_dir = edge_b - edge_a;
|
||||||
|
const Vec3f edge_dir_n = edge_dir.normalized();
|
||||||
|
|
||||||
|
float t1 = (this->first_center - edge_a).dot(edge_dir_n);
|
||||||
|
float t2 = (this->second_center - edge_a).dot(edge_dir_n);
|
||||||
|
Vec3f vector1 = edge_a + t1 * edge_dir_n - this->first_center;
|
||||||
|
Vec3f vector2 = edge_a + t2 * edge_dir_n - this->second_center;
|
||||||
|
|
||||||
|
// Vectors vector1 and vector2 are 3D vector from centers to the intersections. What we want to
|
||||||
|
// measure is length of its projection onto plane perpendicular to dir.
|
||||||
|
if (float dist = vector1.squaredNorm() - std::pow(vector1.dot(this->dir), 2.f); dist < this->radius_sqr && t1 >= 0.f && t1 <= edge_dir.norm())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (float dist = vector2.squaredNorm() - std::pow(vector2.dot(this->dir), 2.f); dist < this->radius_sqr && t2 >= 0.f && t2 <= edge_dir.norm())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if the edge is passing through the rectangle between first_center and second_center.
|
||||||
|
if (edge_inside_rectangle(edge_a, edge_b, rectangle_a, (rectangle_d - rectangle_a)) || edge_inside_rectangle(edge_a, edge_b, rectangle_d, (rectangle_a - rectangle_d)))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -15,7 +15,12 @@ enum class EnforcerBlockerType : int8_t;
|
|||||||
|
|
||||||
// Following class holds information about selected triangles. It also has power
|
// Following class holds information about selected triangles. It also has power
|
||||||
// to recursively subdivide the triangles and make the selection finer.
|
// to recursively subdivide the triangles and make the selection finer.
|
||||||
class TriangleSelector {
|
class TriangleSelector
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
class Triangle;
|
||||||
|
struct Vertex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum CursorType {
|
enum CursorType {
|
||||||
CIRCLE,
|
CIRCLE,
|
||||||
@ -35,6 +40,146 @@ public:
|
|||||||
bool is_mesh_point_clipped(const Vec3f &point) const { return normal.dot(point) - offset > 0.f; }
|
bool is_mesh_point_clipped(const Vec3f &point) const { return normal.dot(point) - offset > 0.f; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Cursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Cursor() = delete;
|
||||||
|
virtual ~Cursor() = default;
|
||||||
|
|
||||||
|
bool is_pointer_in_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_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const = 0;
|
||||||
|
|
||||||
|
static bool is_facet_visible(const Cursor &cursor, int facet_idx, const std::vector<Vec3f> &face_normals);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit Cursor(const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_);
|
||||||
|
|
||||||
|
Transform3f trafo;
|
||||||
|
Vec3f source;
|
||||||
|
|
||||||
|
bool uniform_scaling;
|
||||||
|
Transform3f trafo_normal;
|
||||||
|
float radius;
|
||||||
|
float radius_sqr;
|
||||||
|
Vec3f dir = Vec3f(0.f, 0.f, 0.f);
|
||||||
|
|
||||||
|
ClippingPlane clipping_plane; // Clipping plane to limit painting to not clipped facets only
|
||||||
|
|
||||||
|
friend TriangleSelector;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SinglePointCursor : public Cursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SinglePointCursor() = delete;
|
||||||
|
~SinglePointCursor() override = default;
|
||||||
|
|
||||||
|
bool is_pointer_in_triangle(const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) const override;
|
||||||
|
|
||||||
|
static std::unique_ptr<Cursor> cursor_factory(const Vec3f ¢er, const Vec3f &camera_pos, const float cursor_radius, const CursorType cursor_type, const Transform3d &trafo_matrix, const ClippingPlane &clipping_plane)
|
||||||
|
{
|
||||||
|
assert(cursor_type == TriangleSelector::CursorType::CIRCLE || cursor_type == TriangleSelector::CursorType::SPHERE);
|
||||||
|
if (cursor_type == TriangleSelector::CursorType::SPHERE)
|
||||||
|
return std::make_unique<TriangleSelector::Sphere>(center, camera_pos, cursor_radius, trafo_matrix, clipping_plane);
|
||||||
|
else
|
||||||
|
return std::make_unique<TriangleSelector::Circle>(center, camera_pos, cursor_radius, trafo_matrix, clipping_plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit SinglePointCursor(const Vec3f ¢er_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_);
|
||||||
|
|
||||||
|
Vec3f center;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DoublePointCursor : public Cursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DoublePointCursor() = delete;
|
||||||
|
~DoublePointCursor() override = default;
|
||||||
|
|
||||||
|
bool is_pointer_in_triangle(const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) const override;
|
||||||
|
|
||||||
|
static std::unique_ptr<Cursor> cursor_factory(const Vec3f &first_center, const Vec3f &second_center, const Vec3f &camera_pos, const float cursor_radius, const CursorType cursor_type, const Transform3d &trafo_matrix, const ClippingPlane &clipping_plane)
|
||||||
|
{
|
||||||
|
assert(cursor_type == TriangleSelector::CursorType::CIRCLE || cursor_type == TriangleSelector::CursorType::SPHERE);
|
||||||
|
if (cursor_type == TriangleSelector::CursorType::SPHERE)
|
||||||
|
return std::make_unique<TriangleSelector::Capsule3D>(first_center, second_center, camera_pos, cursor_radius, trafo_matrix, clipping_plane);
|
||||||
|
else
|
||||||
|
return std::make_unique<TriangleSelector::Capsule2D>(first_center, second_center, camera_pos, cursor_radius, trafo_matrix, clipping_plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit DoublePointCursor(const Vec3f &first_center_, const Vec3f &second_center_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_);
|
||||||
|
|
||||||
|
Vec3f first_center;
|
||||||
|
Vec3f second_center;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Sphere : public SinglePointCursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Sphere() = delete;
|
||||||
|
explicit Sphere(const Vec3f ¢er_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_)
|
||||||
|
: SinglePointCursor(center_, source_, radius_world, trafo_, clipping_plane_){};
|
||||||
|
~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_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle : public SinglePointCursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Circle() = delete;
|
||||||
|
explicit Circle(const Vec3f ¢er_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_)
|
||||||
|
: SinglePointCursor(center_, source_, radius_world, trafo_, clipping_plane_){};
|
||||||
|
~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
|
||||||
|
{
|
||||||
|
return TriangleSelector::Cursor::is_facet_visible(*this, facet_idx, face_normals);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Capsule3D : public DoublePointCursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Capsule3D() = delete;
|
||||||
|
explicit Capsule3D(const Vec3f &first_center_, const Vec3f &second_center_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_)
|
||||||
|
: TriangleSelector::DoublePointCursor(first_center_, second_center_, source_, radius_world, trafo_, clipping_plane_)
|
||||||
|
{}
|
||||||
|
~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_facet_visible(int facet_idx, const std::vector<Vec3f> &face_normals) const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Capsule2D : public DoublePointCursor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Capsule2D() = delete;
|
||||||
|
explicit Capsule2D(const Vec3f &first_center_, const Vec3f &second_center_, const Vec3f &source_, float radius_world, const Transform3d &trafo_, const ClippingPlane &clipping_plane_)
|
||||||
|
: TriangleSelector::DoublePointCursor(first_center_, second_center_, source_, radius_world, trafo_, clipping_plane_)
|
||||||
|
{}
|
||||||
|
~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
|
||||||
|
{
|
||||||
|
return TriangleSelector::Cursor::is_facet_visible(*this, facet_idx, face_normals);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||||
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
||||||
|
|
||||||
@ -51,17 +196,12 @@ public:
|
|||||||
[[nodiscard]] int select_unsplit_triangle(const Vec3f &hit, int facet_idx, const Vec3i &neighbors) const;
|
[[nodiscard]] int select_unsplit_triangle(const Vec3f &hit, int facet_idx, const Vec3i &neighbors) const;
|
||||||
|
|
||||||
// Select all triangles fully inside the circle, subdivide where needed.
|
// Select all triangles fully inside the circle, subdivide where needed.
|
||||||
void select_patch(const Vec3f &hit, // point where to start
|
void select_patch(int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
std::unique_ptr<Cursor> &&cursor, // Cursor containing information about the point where to start, camera position (mesh coords), matrix to get from mesh to world, and its shape and type.
|
||||||
const Vec3f &source, // camera position (mesh coords)
|
EnforcerBlockerType new_state, // enforcer or blocker?
|
||||||
float radius, // radius of the cursor
|
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||||
CursorType type, // current type of cursor
|
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
||||||
EnforcerBlockerType new_state, // enforcer or blocker?
|
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.
|
||||||
const Transform3d &trafo, // matrix to get from mesh to world
|
|
||||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
|
||||||
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
|
||||||
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
|
||||||
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
|
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
|
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||||
@ -195,39 +335,16 @@ protected:
|
|||||||
int m_orig_size_vertices = 0;
|
int m_orig_size_vertices = 0;
|
||||||
int m_orig_size_indices = 0;
|
int m_orig_size_indices = 0;
|
||||||
|
|
||||||
// Cache for cursor position, radius and direction.
|
std::unique_ptr<Cursor> m_cursor;
|
||||||
struct Cursor {
|
|
||||||
Cursor() = default;
|
|
||||||
Cursor(const Vec3f& center_, const Vec3f& source_, float radius_world,
|
|
||||||
CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_);
|
|
||||||
bool is_mesh_point_inside(const Vec3f &pt) const;
|
|
||||||
bool is_pointer_in_triangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) const;
|
|
||||||
|
|
||||||
Vec3f center;
|
|
||||||
Vec3f source;
|
|
||||||
Vec3f dir;
|
|
||||||
float radius_sqr;
|
|
||||||
CursorType type;
|
|
||||||
Transform3f trafo;
|
|
||||||
Transform3f trafo_normal;
|
|
||||||
bool uniform_scaling;
|
|
||||||
ClippingPlane clipping_plane;
|
|
||||||
};
|
|
||||||
|
|
||||||
Cursor m_cursor;
|
|
||||||
float m_old_cursor_radius_sqr;
|
float m_old_cursor_radius_sqr;
|
||||||
|
|
||||||
// Private functions:
|
// Private functions:
|
||||||
private:
|
private:
|
||||||
bool select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting);
|
bool select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting);
|
||||||
bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting);
|
bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting);
|
||||||
int vertices_inside(int facet_idx) const;
|
|
||||||
bool faces_camera(int facet) const;
|
|
||||||
void undivide_triangle(int facet_idx);
|
void undivide_triangle(int facet_idx);
|
||||||
void split_triangle(int facet_idx, const Vec3i &neighbors);
|
void split_triangle(int facet_idx, const Vec3i &neighbors);
|
||||||
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
||||||
bool is_pointer_in_triangle(int facet_idx) const;
|
|
||||||
bool is_edge_inside_cursor(int facet_idx) const;
|
|
||||||
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
|
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
|
||||||
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
||||||
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
#include "libslic3r/TriangleMesh.hpp"
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace Slic3r::GUI {
|
namespace Slic3r::GUI {
|
||||||
|
|
||||||
@ -223,6 +224,126 @@ bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point, const Transfo
|
|||||||
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
|
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interpolate points between the previous and current mouse positions, which are then projected onto the object.
|
||||||
|
// Returned projected mouse positions are grouped by mesh_idx. It may contain multiple std::vector<GLGizmoPainterBase::ProjectedMousePosition>
|
||||||
|
// with the same mesh_idx, but all items in std::vector<GLGizmoPainterBase::ProjectedMousePosition> always have the same mesh_idx.
|
||||||
|
std::vector<std::vector<GLGizmoPainterBase::ProjectedMousePosition>> GLGizmoPainterBase::get_projected_mouse_positions(const Vec2d &mouse_position, const double resolution, const std::vector<Transform3d> &trafo_matrices) const
|
||||||
|
{
|
||||||
|
// List of mouse positions that will be used as seeds for painting.
|
||||||
|
std::vector<Vec2d> mouse_positions{mouse_position};
|
||||||
|
if (m_last_mouse_click != Vec2d::Zero()) {
|
||||||
|
// In case current mouse position is far from the last one,
|
||||||
|
// add several positions from between into the list, so there
|
||||||
|
// are no gaps in the painted region.
|
||||||
|
if (size_t patches_in_between = size_t((mouse_position - m_last_mouse_click).norm() / resolution); patches_in_between > 0) {
|
||||||
|
const Vec2d diff = (m_last_mouse_click - mouse_position) / (patches_in_between + 1);
|
||||||
|
for (size_t patch_idx = 1; patch_idx <= patches_in_between; ++patch_idx)
|
||||||
|
mouse_positions.emplace_back(mouse_position + patch_idx * diff);
|
||||||
|
mouse_positions.emplace_back(m_last_mouse_click);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||||
|
std::vector<ProjectedMousePosition> mesh_hit_points;
|
||||||
|
mesh_hit_points.reserve(mouse_position.size());
|
||||||
|
|
||||||
|
// In mesh_hit_points only the last item could have mesh_id == -1, any other items mustn't.
|
||||||
|
for (const Vec2d &mp : mouse_positions) {
|
||||||
|
update_raycast_cache(mp, camera, trafo_matrices);
|
||||||
|
mesh_hit_points.push_back({m_rr.hit, m_rr.mesh_id, m_rr.facet});
|
||||||
|
if (m_rr.mesh_id == -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide mesh_hit_points into groups with the same mesh_idx. It may contain multiple groups with the same mesh_idx.
|
||||||
|
std::vector<std::vector<ProjectedMousePosition>> mesh_hit_points_by_mesh;
|
||||||
|
for (size_t prev_mesh_hit_point = 0, curr_mesh_hit_point = 0; curr_mesh_hit_point < mesh_hit_points.size(); ++curr_mesh_hit_point) {
|
||||||
|
size_t next_mesh_hit_point = curr_mesh_hit_point + 1;
|
||||||
|
if (next_mesh_hit_point >= mesh_hit_points.size() || mesh_hit_points[curr_mesh_hit_point].mesh_idx != mesh_hit_points[next_mesh_hit_point].mesh_idx) {
|
||||||
|
mesh_hit_points_by_mesh.emplace_back();
|
||||||
|
mesh_hit_points_by_mesh.back().insert(mesh_hit_points_by_mesh.back().end(), mesh_hit_points.begin() + int(prev_mesh_hit_point), mesh_hit_points.begin() + int(next_mesh_hit_point));
|
||||||
|
prev_mesh_hit_point = next_mesh_hit_point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto on_same_facet = [](std::vector<ProjectedMousePosition> &hit_points) -> bool {
|
||||||
|
for (const ProjectedMousePosition &mesh_hit_point : hit_points)
|
||||||
|
if (mesh_hit_point.facet_idx != hit_points.front().facet_idx)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Plane
|
||||||
|
{
|
||||||
|
Vec3d origin;
|
||||||
|
Vec3d first_axis;
|
||||||
|
Vec3d second_axis;
|
||||||
|
};
|
||||||
|
auto find_plane = [](std::vector<ProjectedMousePosition> &hit_points) -> std::optional<Plane> {
|
||||||
|
assert(hit_points.size() >= 3);
|
||||||
|
for (size_t third_idx = 2; third_idx < hit_points.size(); ++third_idx) {
|
||||||
|
const Vec3d &first_point = hit_points[third_idx - 2].mesh_hit.cast<double>();
|
||||||
|
const Vec3d &second_point = hit_points[third_idx - 1].mesh_hit.cast<double>();
|
||||||
|
const Vec3d &third_point = hit_points[third_idx].mesh_hit.cast<double>();
|
||||||
|
|
||||||
|
const Vec3d first_vec = first_point - second_point;
|
||||||
|
const Vec3d second_vec = third_point - second_point;
|
||||||
|
|
||||||
|
// If three points aren't collinear, then there exists only one plane going through all points.
|
||||||
|
if (first_vec.cross(second_vec).squaredNorm() > sqr(EPSILON)) {
|
||||||
|
const Vec3d first_axis_vec_n = first_vec.normalized();
|
||||||
|
// Make second_vec perpendicular to first_axis_vec_n using Gram–Schmidt orthogonalization process
|
||||||
|
const Vec3d second_axis_vec_n = (second_vec - (first_vec.dot(second_vec) / first_vec.dot(first_vec)) * first_vec).normalized();
|
||||||
|
return Plane{second_point, first_axis_vec_n, second_axis_vec_n};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(std::vector<ProjectedMousePosition> &hit_points : mesh_hit_points_by_mesh) {
|
||||||
|
assert(!hit_points.empty());
|
||||||
|
if (hit_points.back().mesh_idx == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (hit_points.size() <= 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (on_same_facet(hit_points)) {
|
||||||
|
hit_points = {hit_points.front(), hit_points.back()};
|
||||||
|
} else if (std::optional<Plane> plane = find_plane(hit_points); plane) {
|
||||||
|
Polyline polyline;
|
||||||
|
polyline.points.reserve(hit_points.size());
|
||||||
|
// Project hit_points into its plane to simplified them in the next step.
|
||||||
|
for (auto &hit_point : hit_points) {
|
||||||
|
const Vec3d &point = hit_point.mesh_hit.cast<double>();
|
||||||
|
const double x_cord = plane->first_axis.dot(point - plane->origin);
|
||||||
|
const double y_cord = plane->second_axis.dot(point - plane->origin);
|
||||||
|
polyline.points.emplace_back(scale_(x_cord), scale_(y_cord));
|
||||||
|
}
|
||||||
|
|
||||||
|
polyline.simplify(scale_(m_cursor_radius) / 10.);
|
||||||
|
|
||||||
|
const int mesh_idx = hit_points.front().mesh_idx;
|
||||||
|
std::vector<ProjectedMousePosition> new_hit_points;
|
||||||
|
new_hit_points.reserve(polyline.points.size());
|
||||||
|
// Project 2D simplified hit_points beck to 3D.
|
||||||
|
for (const Point &point : polyline.points) {
|
||||||
|
const double x_cord = unscale<double>(point.x());
|
||||||
|
const double y_cord = unscale<double>(point.y());
|
||||||
|
const Vec3d new_hit_point = plane->origin + x_cord * plane->first_axis + y_cord * plane->second_axis;
|
||||||
|
const int facet_idx = m_c->raycaster()->raycasters()[mesh_idx]->get_closest_facet(new_hit_point.cast<float>());
|
||||||
|
new_hit_points.push_back({new_hit_point.cast<float>(), mesh_idx, size_t(facet_idx)});
|
||||||
|
}
|
||||||
|
|
||||||
|
hit_points = new_hit_points;
|
||||||
|
} else {
|
||||||
|
hit_points = {hit_points.front(), hit_points.back()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mesh_hit_points_by_mesh;
|
||||||
|
}
|
||||||
|
|
||||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||||
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
|
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
|
||||||
@ -295,28 +416,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
const Transform3d instance_trafo = mi->get_transformation().get_matrix();
|
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);
|
||||||
|
|
||||||
// List of mouse positions that will be used as seeds for painting.
|
|
||||||
std::vector<Vec2d> mouse_positions{mouse_position};
|
|
||||||
|
|
||||||
// In case current mouse position is far from the last one,
|
|
||||||
// add several positions from between into the list, so there
|
|
||||||
// are no gaps in the painted region.
|
|
||||||
{
|
|
||||||
if (m_last_mouse_click == Vec2d::Zero())
|
|
||||||
m_last_mouse_click = mouse_position;
|
|
||||||
// resolution describes minimal distance limit using circle radius
|
|
||||||
// as a unit (e.g., 2 would mean the patches will be touching).
|
|
||||||
double resolution = 0.7;
|
|
||||||
double diameter_px = resolution * m_cursor_radius * camera.get_zoom();
|
|
||||||
int patches_in_between = int(((mouse_position - m_last_mouse_click).norm() - diameter_px) / diameter_px);
|
|
||||||
if (patches_in_between > 0) {
|
|
||||||
Vec2d diff = (mouse_position - m_last_mouse_click)/(patches_in_between+1);
|
|
||||||
for (int i=1; i<=patches_in_between; ++i)
|
|
||||||
mouse_positions.emplace_back(m_last_mouse_click + i*diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved
|
|
||||||
|
|
||||||
// Precalculate transformations of individual meshes.
|
// Precalculate transformations of individual meshes.
|
||||||
std::vector<Transform3d> trafo_matrices;
|
std::vector<Transform3d> trafo_matrices;
|
||||||
std::vector<Transform3d> trafo_matrices_not_translate;
|
std::vector<Transform3d> trafo_matrices_not_translate;
|
||||||
@ -326,50 +425,70 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true));
|
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now "click" into all the prepared points and spill paint around them.
|
std::vector<std::vector<ProjectedMousePosition>> projected_mouse_positions_by_mesh = get_projected_mouse_positions(mouse_position, 1., trafo_matrices);
|
||||||
for (const Vec2d& mp : mouse_positions) {
|
m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved
|
||||||
update_raycast_cache(mp, camera, trafo_matrices);
|
|
||||||
|
|
||||||
bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None);
|
for (const std::vector<ProjectedMousePosition> &projected_mouse_positions : projected_mouse_positions_by_mesh) {
|
||||||
|
assert(!projected_mouse_positions.empty());
|
||||||
|
const int mesh_idx = projected_mouse_positions.front().mesh_idx;
|
||||||
|
const bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None);
|
||||||
|
|
||||||
// The mouse button click detection is enabled when there is a valid hit.
|
// The mouse button click detection is enabled when there is a valid hit.
|
||||||
// Missing the object entirely
|
// Missing the object entirely
|
||||||
// shall not capture the mouse.
|
// shall not capture the mouse.
|
||||||
if (m_rr.mesh_id != -1) {
|
if (mesh_idx != -1)
|
||||||
if (m_button_down == Button::None)
|
if (m_button_down == Button::None)
|
||||||
m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
|
m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
|
||||||
}
|
|
||||||
|
|
||||||
if (m_rr.mesh_id == -1) {
|
// In case we have no valid hit, we can return. The event will be stopped when
|
||||||
// In case we have no valid hit, we can return. The event will be stopped when
|
// dragging while painting (to prevent scene rotations and moving the object)
|
||||||
// dragging while painting (to prevent scene rotations and moving the object)
|
if (mesh_idx == -1)
|
||||||
return dragging_while_painting;
|
return dragging_while_painting;
|
||||||
}
|
|
||||||
|
|
||||||
const Transform3d &trafo_matrix = trafo_matrices[m_rr.mesh_id];
|
const Transform3d &trafo_matrix = trafo_matrices[mesh_idx];
|
||||||
const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[m_rr.mesh_id];
|
const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[mesh_idx];
|
||||||
|
|
||||||
// Calculate direction from camera to the hit (in mesh coords):
|
// Calculate direction from camera to the hit (in mesh coords):
|
||||||
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
||||||
|
|
||||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
assert(mesh_idx < int(m_triangle_selectors.size()));
|
||||||
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
||||||
if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
||||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state);
|
for(const ProjectedMousePosition &projected_mouse_position : projected_mouse_positions) {
|
||||||
if (m_tool_type == ToolType::SMART_FILL)
|
assert(projected_mouse_position.mesh_idx == mesh_idx);
|
||||||
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,
|
const Vec3f mesh_hit = projected_mouse_position.mesh_hit;
|
||||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
const int facet_idx = int(projected_mouse_position.facet_idx);
|
||||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state);
|
||||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false, true);
|
if (m_tool_type == ToolType::SMART_FILL)
|
||||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle,
|
||||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true, true);
|
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_seed_fill_last_mesh_id = -1;
|
m_seed_fill_last_mesh_id = -1;
|
||||||
} else if (m_tool_type == ToolType::BRUSH)
|
}
|
||||||
m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
|
} else if (m_tool_type == ToolType::BRUSH) {
|
||||||
new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled, clp,
|
assert(m_cursor_type == TriangleSelector::CursorType::CIRCLE || m_cursor_type == TriangleSelector::CursorType::SPHERE);
|
||||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
|
||||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
if (projected_mouse_positions.size() == 1) {
|
||||||
|
const ProjectedMousePosition &first_position = projected_mouse_positions.front();
|
||||||
|
std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::SinglePointCursor::cursor_factory(first_position.mesh_hit,
|
||||||
|
camera_pos, m_cursor_radius,
|
||||||
|
m_cursor_type, trafo_matrix, clp);
|
||||||
|
m_triangle_selectors[mesh_idx]->select_patch(int(first_position.facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate,
|
||||||
|
m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||||
|
} else {
|
||||||
|
for (auto first_position_it = projected_mouse_positions.cbegin(); first_position_it != projected_mouse_positions.cend() - 1; ++first_position_it) {
|
||||||
|
auto second_position_it = first_position_it + 1;
|
||||||
|
std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::DoublePointCursor::cursor_factory(first_position_it->mesh_hit, second_position_it->mesh_hit, camera_pos, m_cursor_radius, m_cursor_type, trafo_matrix, clp);
|
||||||
|
m_triangle_selectors[mesh_idx]->select_patch(int(first_position_it->facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_triangle_selectors[mesh_idx]->request_update_render_data();
|
||||||
m_last_mouse_click = mouse_position;
|
m_last_mouse_click = mouse_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +156,13 @@ protected:
|
|||||||
SMART_FILL
|
SMART_FILL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ProjectedMousePosition
|
||||||
|
{
|
||||||
|
Vec3f mesh_hit;
|
||||||
|
int mesh_idx;
|
||||||
|
size_t facet_idx;
|
||||||
|
};
|
||||||
|
|
||||||
bool m_triangle_splitting_enabled = true;
|
bool m_triangle_splitting_enabled = true;
|
||||||
ToolType m_tool_type = ToolType::BRUSH;
|
ToolType m_tool_type = ToolType::BRUSH;
|
||||||
float m_smart_fill_angle = 30.f;
|
float m_smart_fill_angle = 30.f;
|
||||||
@ -188,6 +195,8 @@ protected:
|
|||||||
TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const;
|
TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::vector<std::vector<ProjectedMousePosition>> get_projected_mouse_positions(const Vec2d &mouse_position, double resolution, const std::vector<Transform3d> &trafo_matrices) const;
|
||||||
|
|
||||||
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
||||||
void update_raycast_cache(const Vec2d& mouse_position,
|
void update_raycast_cache(const Vec2d& mouse_position,
|
||||||
const Camera& camera,
|
const Camera& camera,
|
||||||
|
@ -304,7 +304,13 @@ Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
|
|||||||
return closest_point.cast<float>();
|
return closest_point.cast<float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MeshRaycaster::get_closest_facet(const Vec3f &point) const
|
||||||
|
{
|
||||||
|
int facet_idx = 0;
|
||||||
|
Vec3d closest_point;
|
||||||
|
m_emesh.squared_distance(point.cast<double>(), facet_idx, closest_point);
|
||||||
|
return facet_idx;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -151,6 +151,9 @@ public:
|
|||||||
|
|
||||||
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
||||||
|
|
||||||
|
// Given a point in mesh coords, the method returns the closest facet from mesh.
|
||||||
|
int get_closest_facet(const Vec3f &point) const;
|
||||||
|
|
||||||
Vec3f get_triangle_normal(size_t facet_idx) const;
|
Vec3f get_triangle_normal(size_t facet_idx) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
Reference in New Issue
Block a user