Group hollowing result (including grid) into one struct
This commit is contained in:
parent
e57eca0289
commit
82954ba715
@ -26,11 +26,36 @@ inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); }
|
|||||||
template<class S, class = FloatingOnly<S>>
|
template<class S, class = FloatingOnly<S>>
|
||||||
inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; }
|
inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; }
|
||||||
|
|
||||||
static TriangleMesh _generate_interior(const TriangleMesh &mesh,
|
struct Interior {
|
||||||
const JobController &ctl,
|
TriangleMesh mesh;
|
||||||
double min_thickness,
|
openvdb::FloatGrid::Ptr gridptr;
|
||||||
double voxel_scale,
|
double closing_distance = 0.;
|
||||||
double closing_dist)
|
double thickness = 0.;
|
||||||
|
double voxel_scale = 1.;
|
||||||
|
double nb_in = 3.;
|
||||||
|
double nb_out = 3.;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InteriorDeleter::operator()(Interior *p)
|
||||||
|
{
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriangleMesh &get_mesh(Interior &interior)
|
||||||
|
{
|
||||||
|
return interior.mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TriangleMesh &get_mesh(const Interior &interior)
|
||||||
|
{
|
||||||
|
return interior.mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
|
||||||
|
const JobController &ctl,
|
||||||
|
double min_thickness,
|
||||||
|
double voxel_scale,
|
||||||
|
double closing_dist)
|
||||||
{
|
{
|
||||||
double offset = voxel_scale * min_thickness;
|
double offset = voxel_scale * min_thickness;
|
||||||
double D = voxel_scale * closing_dist;
|
double D = voxel_scale * closing_dist;
|
||||||
@ -64,22 +89,30 @@ static TriangleMesh _generate_interior(const TriangleMesh &mesh,
|
|||||||
else ctl.statuscb(70, L("Hollowing"));
|
else ctl.statuscb(70, L("Hollowing"));
|
||||||
|
|
||||||
double adaptivity = 0.;
|
double adaptivity = 0.;
|
||||||
|
InteriorPtr interior = InteriorPtr{new Interior{}};
|
||||||
|
|
||||||
auto omesh = grid_to_mesh(*gridptr, iso_surface, adaptivity);
|
interior->mesh = grid_to_mesh(*gridptr, iso_surface, adaptivity);
|
||||||
|
interior->gridptr = gridptr;
|
||||||
|
|
||||||
if (ctl.stopcondition()) return {};
|
if (ctl.stopcondition()) return {};
|
||||||
else ctl.statuscb(100, L("Hollowing"));
|
else ctl.statuscb(100, L("Hollowing"));
|
||||||
|
|
||||||
return omesh;
|
interior->closing_distance = D;
|
||||||
|
interior->thickness = offset;
|
||||||
|
interior->voxel_scale = voxel_scale;
|
||||||
|
interior->nb_in = narrowb;
|
||||||
|
interior->nb_out = narrowb;
|
||||||
|
|
||||||
|
return interior;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh & mesh,
|
InteriorPtr generate_interior(const TriangleMesh & mesh,
|
||||||
const HollowingConfig &hc,
|
const HollowingConfig &hc,
|
||||||
const JobController & ctl)
|
const JobController & ctl)
|
||||||
{
|
{
|
||||||
static const double MIN_OVERSAMPL = 3.;
|
static const double MIN_OVERSAMPL = 3.;
|
||||||
static const double MAX_OVERSAMPL = 8.;
|
static const double MAX_OVERSAMPL = 8.;
|
||||||
|
|
||||||
// I can't figure out how to increase the grid resolution through openvdb
|
// I can't figure out how to increase the grid resolution through openvdb
|
||||||
// API so the model will be scaled up before conversion and the result
|
// API so the model will be scaled up before conversion and the result
|
||||||
// scaled down. Voxels have a unit size. If I set voxelSize smaller, it
|
// scaled down. Voxels have a unit size. If I set voxelSize smaller, it
|
||||||
@ -88,26 +121,29 @@ std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh & mesh,
|
|||||||
//
|
//
|
||||||
// max 8x upscale, min is native voxel size
|
// max 8x upscale, min is native voxel size
|
||||||
auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality;
|
auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality;
|
||||||
auto meshptr = std::make_unique<TriangleMesh>(
|
|
||||||
_generate_interior(mesh, ctl, hc.min_thickness, voxel_scale,
|
InteriorPtr interior =
|
||||||
hc.closing_distance));
|
generate_interior_verbose(mesh, ctl, hc.min_thickness, voxel_scale,
|
||||||
|
hc.closing_distance);
|
||||||
if (meshptr && !meshptr->empty()) {
|
|
||||||
|
if (interior && !interior->mesh.empty()) {
|
||||||
|
|
||||||
// This flips the normals to be outward facing...
|
// This flips the normals to be outward facing...
|
||||||
meshptr->require_shared_vertices();
|
interior->mesh.require_shared_vertices();
|
||||||
indexed_triangle_set its = std::move(meshptr->its);
|
indexed_triangle_set its = std::move(interior->mesh.its);
|
||||||
|
|
||||||
Slic3r::simplify_mesh(its);
|
Slic3r::simplify_mesh(its);
|
||||||
|
|
||||||
// flip normals back...
|
// flip normals back...
|
||||||
for (stl_triangle_vertex_indices &ind : its.indices)
|
for (stl_triangle_vertex_indices &ind : its.indices)
|
||||||
std::swap(ind(0), ind(2));
|
std::swap(ind(0), ind(2));
|
||||||
|
|
||||||
*meshptr = Slic3r::TriangleMesh{its};
|
interior->mesh = Slic3r::TriangleMesh{its};
|
||||||
|
interior->mesh.repaired = true;
|
||||||
|
interior->mesh.require_shared_vertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
return meshptr;
|
return interior;
|
||||||
}
|
}
|
||||||
|
|
||||||
Contour3D DrainHole::to_mesh() const
|
Contour3D DrainHole::to_mesh() const
|
||||||
@ -269,12 +305,22 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
|||||||
obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]);
|
obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg)
|
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags)
|
||||||
{
|
{
|
||||||
std::unique_ptr<Slic3r::TriangleMesh> inter_ptr =
|
InteriorPtr interior = generate_interior(mesh, cfg, JobController{});
|
||||||
Slic3r::sla::generate_interior(mesh);
|
if (!interior) return;
|
||||||
|
|
||||||
if (inter_ptr) mesh.merge(*inter_ptr);
|
hollow_mesh(mesh, *interior, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags)
|
||||||
|
{
|
||||||
|
if (mesh.empty() || interior.mesh.empty()) return;
|
||||||
|
|
||||||
|
// if (flags & hfRemoveInsideTriangles && interior.gridptr)
|
||||||
|
// erase_inside_triangles_2(mesh, interior);
|
||||||
|
|
||||||
|
mesh.merge(interior.mesh);
|
||||||
mesh.require_shared_vertices();
|
mesh.require_shared_vertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,17 @@ struct HollowingConfig
|
|||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum HollowingFlags { hfRemoveInsideTriangles = 0x1 };
|
||||||
|
|
||||||
|
// All data related to a generated mesh interior. Includes the 3D grid and mesh
|
||||||
|
// and various metadata. No need to manipulate from outside.
|
||||||
|
struct Interior;
|
||||||
|
struct InteriorDeleter { void operator()(Interior *p); };
|
||||||
|
using InteriorPtr = std::unique_ptr<Interior, InteriorDeleter>;
|
||||||
|
|
||||||
|
TriangleMesh & get_mesh(Interior &interior);
|
||||||
|
const TriangleMesh &get_mesh(const Interior &interior);
|
||||||
|
|
||||||
struct DrainHole
|
struct DrainHole
|
||||||
{
|
{
|
||||||
Vec3f pos;
|
Vec3f pos;
|
||||||
@ -60,11 +71,15 @@ using DrainHoles = std::vector<DrainHole>;
|
|||||||
|
|
||||||
constexpr float HoleStickOutLength = 1.f;
|
constexpr float HoleStickOutLength = 1.f;
|
||||||
|
|
||||||
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh &mesh,
|
InteriorPtr generate_interior(const TriangleMesh &mesh,
|
||||||
const HollowingConfig & = {},
|
const HollowingConfig & = {},
|
||||||
const JobController &ctl = {});
|
const JobController &ctl = {});
|
||||||
|
|
||||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg);
|
// Will do the hollowing
|
||||||
|
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 cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
||||||
const std::vector<float> &slicegrid,
|
const std::vector<float> &slicegrid,
|
||||||
|
@ -1149,8 +1149,9 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const
|
|||||||
|
|
||||||
const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const
|
const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const
|
||||||
{
|
{
|
||||||
if (m_hollowing_data && m_config.hollowing_enable.getBool())
|
if (m_hollowing_data && m_hollowing_data->interior &&
|
||||||
return m_hollowing_data->interior;
|
m_config.hollowing_enable.getBool())
|
||||||
|
return sla::get_mesh(*m_hollowing_data->interior);
|
||||||
|
|
||||||
return EMPTY_MESH;
|
return EMPTY_MESH;
|
||||||
}
|
}
|
||||||
|
@ -327,8 +327,8 @@ private:
|
|||||||
class HollowingData
|
class HollowingData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TriangleMesh interior;
|
sla::InteriorPtr interior;
|
||||||
mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh
|
mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,13 +131,14 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
|||||||
double quality = po.m_config.hollowing_quality.getFloat();
|
double quality = po.m_config.hollowing_quality.getFloat();
|
||||||
double closing_d = po.m_config.hollowing_closing_distance.getFloat();
|
double closing_d = po.m_config.hollowing_closing_distance.getFloat();
|
||||||
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
|
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
|
||||||
auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg);
|
|
||||||
|
|
||||||
if (meshptr->empty())
|
sla::InteriorPtr interior = generate_interior(po.transformed_mesh(), hlwcfg);
|
||||||
|
|
||||||
|
if (!interior || sla::get_mesh(*interior).empty())
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
|
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
|
||||||
else {
|
else {
|
||||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||||
po.m_hollowing_data->interior = *meshptr;
|
po.m_hollowing_data->interior = std::move(interior);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +146,9 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
|||||||
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||||
{
|
{
|
||||||
bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty();
|
bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty();
|
||||||
bool is_hollowed = (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty());
|
bool is_hollowed =
|
||||||
|
(po.m_hollowing_data && po.m_hollowing_data->interior &&
|
||||||
|
!sla::get_mesh(*po.m_hollowing_data->interior).empty());
|
||||||
|
|
||||||
if (! is_hollowed && ! needs_drilling) {
|
if (! is_hollowed && ! needs_drilling) {
|
||||||
// In this case we can dump any data that might have been
|
// In this case we can dump any data that might have been
|
||||||
@ -163,10 +166,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
|||||||
// holes that are no longer on the frontend.
|
// holes that are no longer on the frontend.
|
||||||
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||||
hollowed_mesh = po.transformed_mesh();
|
hollowed_mesh = po.transformed_mesh();
|
||||||
if (! po.m_hollowing_data->interior.empty()) {
|
sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior/*, sla::hfRemoveInsideTriangles*/);
|
||||||
hollowed_mesh.merge(po.m_hollowing_data->interior);
|
|
||||||
hollowed_mesh.require_shared_vertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! needs_drilling) {
|
if (! needs_drilling) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
|
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
|
||||||
@ -260,9 +260,15 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
|||||||
auto &slice_grid = po.m_model_height_levels;
|
auto &slice_grid = po.m_model_height_levels;
|
||||||
slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr);
|
slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr);
|
||||||
|
|
||||||
if (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()) {
|
sla::Interior *interior = po.m_hollowing_data ?
|
||||||
po.m_hollowing_data->interior.repair(true);
|
po.m_hollowing_data->interior.get() :
|
||||||
TriangleMeshSlicer interior_slicer(&po.m_hollowing_data->interior);
|
nullptr;
|
||||||
|
|
||||||
|
if (interior && ! sla::get_mesh(*interior).empty()) {
|
||||||
|
TriangleMesh interiormesh = sla::get_mesh(*interior);
|
||||||
|
interiormesh.repaired = false;
|
||||||
|
interiormesh.repair(true);
|
||||||
|
TriangleMeshSlicer interior_slicer(&interiormesh);
|
||||||
std::vector<ExPolygons> interior_slices;
|
std::vector<ExPolygons> interior_slices;
|
||||||
interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr);
|
interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr);
|
||||||
|
|
||||||
|
@ -26,21 +26,19 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]")
|
TEST_CASE("Hollow two overlapping spheres") {
|
||||||
{
|
using namespace Slic3r;
|
||||||
Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj");
|
|
||||||
Benchmark bench;
|
TriangleMesh sphere1 = make_sphere(10., 2 * PI / 20.), sphere2 = sphere1;
|
||||||
bench.start();
|
|
||||||
|
sphere1.translate(-5.f, 0.f, 0.f);
|
||||||
std::unique_ptr<Slic3r::TriangleMesh> out_mesh_ptr =
|
sphere2.translate( 5.f, 0.f, 0.f);
|
||||||
Slic3r::sla::generate_interior(in_mesh);
|
|
||||||
|
sphere1.merge(sphere2);
|
||||||
bench.stop();
|
sphere1.require_shared_vertices();
|
||||||
|
|
||||||
std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl;
|
sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles);
|
||||||
|
|
||||||
if (out_mesh_ptr) in_mesh.merge(*out_mesh_ptr);
|
sphere1.WriteOBJFile("twospheres.obj");
|
||||||
in_mesh.require_shared_vertices();
|
|
||||||
in_mesh.WriteOBJFile("merged_out.obj");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,9 +88,9 @@ void test_supports(const std::string &obj_filename,
|
|||||||
REQUIRE_FALSE(mesh.empty());
|
REQUIRE_FALSE(mesh.empty());
|
||||||
|
|
||||||
if (hollowingcfg.enabled) {
|
if (hollowingcfg.enabled) {
|
||||||
auto inside = sla::generate_interior(mesh, hollowingcfg);
|
sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg);
|
||||||
REQUIRE(inside);
|
REQUIRE(interior);
|
||||||
mesh.merge(*inside);
|
mesh.merge(sla::get_mesh(*interior));
|
||||||
mesh.require_shared_vertices();
|
mesh.require_shared_vertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user