Exclude triangles of original interior mesh and drillholes from trimming

This commit is contained in:
tamasmeszaros 2021-03-03 09:29:13 +01:00
parent fbc758642b
commit a62262666a
5 changed files with 184 additions and 6 deletions

View file

@ -421,7 +421,8 @@ void divide_triangle(const DivFace &face, Fn &&visitor)
divide_triangle(child2, std::forward<Fn>(visitor));
}
void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior)
void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
const std::vector<bool> &exclude_mask)
{
enum TrPos { posInside, posTouch, posOutside };
@ -429,6 +430,11 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior)
auto &vertices = mesh.its.vertices;
auto bb = mesh.bounding_box();
bool use_exclude_mask = faces.size() == exclude_mask.size();
auto is_excluded = [&exclude_mask, use_exclude_mask](size_t face_id) {
return use_exclude_mask && exclude_mask[face_id];
};
// TODO: Parallel mode not working yet
using exec_policy = ccr_seq;
@ -518,6 +524,10 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior)
exec_policy::for_each(size_t(0), faces.size(), [&] (size_t face_idx) {
const Vec3i &face = faces[face_idx];
// If the triangle is excluded, we need to keep it.
if (is_excluded(face_idx))
return;
std::array<Vec3f, 3> pts =
{ vertices[face(0)], vertices[face(1)], vertices[face(2)] };

View file

@ -81,7 +81,16 @@ void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0);
// Hollowing prepared in "interior", merge with original mesh
void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0);
void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior);
void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
const std::vector<bool> &exclude_mask = {});
double get_distance(const Vec3f &p, const Interior &interior);
template<class T>
FloatingOnly<T> get_distance(const Vec<3, T> &p, const Interior &interior)
{
return get_distance(Vec3f(p.template cast<float>()), interior);
}
void cut_drainholes(std::vector<ExPolygons> & obj_slices,
const std::vector<float> &slicegrid,

View file

@ -1,3 +1,5 @@
#include <unordered_set>
#include <libslic3r/Exception.hpp>
#include <libslic3r/SLAPrintSteps.hpp>
#include <libslic3r/MeshBoolean.hpp>
@ -142,6 +144,136 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
}
}
struct FaceHash {
// A hash is created for each triangle to be identifiable. The hash uses
// only the triangle's geometric traits, not the index in a particular mesh.
std::unordered_set<std::string> facehash;
static std::string facekey(const Vec3i &face,
const std::vector<Vec3f> &vertices)
{
// Scale to integer to avoid floating points
std::array<Vec<3, int64_t>, 3> pts = {
scaled<int64_t>(vertices[face(0)]),
scaled<int64_t>(vertices[face(1)]),
scaled<int64_t>(vertices[face(2)])
};
// Get the first two sides of the triangle, do a cross product and move
// that vector to the center of the triangle. This encodes all
// information to identify an identical triangle at the same position.
Vec<3, int64_t> a = pts[0] - pts[2], b = pts[1] - pts[2];
Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3;
// Return a concatenated string representation of the coordinates
return std::to_string(c(0)) + std::to_string(c(1)) + std::to_string(c(2));
};
FaceHash(const indexed_triangle_set &its)
{
for (const Vec3i &face : its.indices) {
std::string keystr = facekey(face, its.vertices);
facehash.insert(keystr);
}
}
bool find(const std::string &key)
{
auto it = facehash.find(key);
return it != facehash.end();
}
};
// Create exclude mask for triangle removal inside hollowed interiors.
// This is necessary when the interior is already part of the mesh which was
// drilled using CGAL mesh boolean operation. Excluded will be the triangles
// originally part of the interior mesh and triangles that make up the drilled
// hole walls.
static std::vector<bool> create_exclude_mask(
const indexed_triangle_set &its,
const sla::Interior &interior,
const std::vector<sla::DrainHole> &holes)
{
FaceHash interior_hash{sla::get_mesh(interior).its};
std::vector<bool> exclude_mask(its.indices.size(), false);
std::vector< std::vector<size_t> > neighbor_index =
create_neighbor_index(its);
auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face)
{
for (int i = 0; i < 3; ++i) {
const std::vector<size_t> &neighbors = neighbor_index[face(i)];
for (size_t fi_n : neighbors) exclude_mask[fi_n] = true;
}
};
for (size_t fi = 0; fi < its.indices.size(); ++fi) {
auto &face = its.indices[fi];
std::string key =
FaceHash::facekey(face, its.vertices);
if (interior_hash.find(key)) {
exclude_mask[fi] = true;
continue;
}
if (exclude_mask[fi]) {
exclude_neighbors(face);
continue;
}
// Lets deal with the holes. All the triangles of a hole and all the
// neighbors of these triangles need to be kept. The neigbors were
// created by CGAL mesh boolean operation that modified the original
// interior inside the input mesh to contain the holes.
Vec3d tr_center = (
its.vertices[face(0)] +
its.vertices[face(1)] +
its.vertices[face(2)]
).cast<double>() / 3.;
// If the center is more than half a mm inside the interior,
// it cannot possibly be part of a hole wall.
if (sla::get_distance(tr_center, interior) < -0.5)
continue;
Vec3f U = its.vertices[face(1)] - its.vertices[face(0)];
Vec3f V = its.vertices[face(2)] - its.vertices[face(0)];
Vec3f C = U.cross(V);
Vec3f face_normal = C.normalized();
for (const sla::DrainHole &dh : holes) {
Vec3d dhpos = dh.pos.cast<double>();
Vec3d dhend = dhpos + dh.normal.cast<double>() * dh.height;
Linef3 holeaxis{dhpos, dhend};
double D_hole_center = line_alg::distance_to(holeaxis, tr_center);
double D_hole = std::abs(D_hole_center - dh.radius);
float dot = dh.normal.dot(face_normal);
// Empiric tolerances for center distance and normals angle.
// For triangles that are part of a hole wall the angle of
// triangle normal and the hole axis is around 90 degrees,
// so the dot product is around zero.
double D_tol = dh.radius / sla::DrainHole::steps;
float normal_angle_tol = 1.f / sla::DrainHole::steps;
if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) {
exclude_mask[fi] = true;
exclude_neighbors(face);
}
}
}
return exclude_mask;
}
// Drill holes into the hollowed/original mesh.
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
{
@ -207,10 +339,13 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
mesh_view = hollowed_mesh;
if (is_hollowed)
sla::remove_inside_triangles(mesh_view,
*po.m_hollowing_data->interior,
drainholes);
if (is_hollowed) {
auto &interior = *po.m_hollowing_data->interior;
std::vector<bool> exclude_mask =
create_exclude_mask(mesh_view.its, interior, drainholes);
sla::remove_inside_triangles(mesh_view, interior, exclude_mask);
}
} catch (const std::runtime_error &) {
throw Slic3r::SlicingError(L(

View file

@ -2063,4 +2063,22 @@ TriangleMesh make_sphere(double radius, double fa)
return mesh;
}
std::vector<std::vector<size_t> > create_neighbor_index(const indexed_triangle_set &its)
{
if (its.vertices.empty()) return {};
size_t res = its.indices.size() / its.vertices.size();
std::vector< std::vector<size_t> > index(its.vertices.size(),
reserve_vector<size_t>(res));
for (size_t fi = 0; fi < its.indices.size(); ++fi) {
auto &face = its.indices[fi];
index[face(0)].emplace_back(fi);
index[face(1)].emplace_back(fi);
index[face(2)].emplace_back(fi);
}
return index;
}
}

View file

@ -89,6 +89,12 @@ private:
std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
};
// Create an index of faces belonging to each vertex. The returned vector can
// be indexed with vertex indices and contains a list of face indices for each
// vertex.
std::vector< std::vector<size_t> >
create_neighbor_index(const indexed_triangle_set &its);
enum FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges.
feGeneral,