mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-03 04:20:37 +08:00
wip
This commit is contained in:
parent
2144f81bf1
commit
c448b31204
@ -1,13 +1,7 @@
|
||||
#include "SLAPrint.hpp"
|
||||
#include "SLAPrintSteps.hpp"
|
||||
|
||||
#include "Format/SL1.hpp"
|
||||
#include "Format/SL1_SVG.hpp"
|
||||
#include "Format/pwmx.hpp"
|
||||
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
@ -388,7 +382,12 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
||||
it_print_object_status = print_object_status.end();
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||
bool model_parts_differ =
|
||||
model_volume_list_changed(model_object, model_object_new,
|
||||
{ModelVolumeType::MODEL_PART,
|
||||
ModelVolumeType::NEGATIVE_VOLUME,
|
||||
ModelVolumeType::SUPPORT_ENFORCER,
|
||||
ModelVolumeType::SUPPORT_BLOCKER});
|
||||
bool sla_trafo_differs =
|
||||
model_object.instances.empty() != model_object_new.instances.empty() ||
|
||||
(! model_object.instances.empty() &&
|
||||
@ -597,7 +596,7 @@ void SLAPrint::process()
|
||||
|
||||
// We want to first process all objects...
|
||||
std::vector<SLAPrintObjectStep> level1_obj_steps = {
|
||||
slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad
|
||||
slaposAssembly, slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad
|
||||
};
|
||||
|
||||
// and then slice all supports to allow preview to be displayed ASAP
|
||||
@ -793,12 +792,13 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
||||
|
||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
|
||||
: Inherited(print, model_object)
|
||||
, m_transformed_rmesh([this](TriangleMesh &obj) {
|
||||
obj = m_model_object->raw_mesh();
|
||||
if (!obj.empty()) {
|
||||
obj.transform(m_trafo);
|
||||
}
|
||||
})
|
||||
// , m_transformed_rmesh([this](TriangleMesh &obj) {
|
||||
//// obj = m_model_object->raw_mesh();
|
||||
//// if (!obj.empty()) {
|
||||
//// obj.transform(m_trafo);
|
||||
//// }
|
||||
// obj = transformed_mesh_csg(*this);
|
||||
// })
|
||||
{}
|
||||
|
||||
SLAPrintObject::~SLAPrintObject() {}
|
||||
@ -882,8 +882,10 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
||||
{
|
||||
bool invalidated = Inherited::invalidate_step(step);
|
||||
// propagate to dependent steps
|
||||
if (step == slaposHollowing) {
|
||||
if (step == slaposAssembly) {
|
||||
invalidated |= this->invalidate_all_steps();
|
||||
} else if (step == slaposHollowing) {
|
||||
invalidated |= invalidated |= this->invalidate_steps({ slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
||||
} else if (step == slaposDrillHoles) {
|
||||
invalidated |= this->invalidate_steps({ slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
@ -907,7 +909,7 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
||||
|
||||
bool SLAPrintObject::invalidate_all_steps()
|
||||
{
|
||||
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||
return Inherited::invalidate_all_steps() || m_print->invalidate_all_steps();
|
||||
}
|
||||
|
||||
double SLAPrintObject::get_elevation() const {
|
||||
@ -1001,8 +1003,12 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
|
||||
bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const
|
||||
{
|
||||
switch (step) {
|
||||
case slaposAssembly:
|
||||
return true;
|
||||
case slaposObjectSlice:
|
||||
return ! this->m_mesh_from_slices.empty();
|
||||
case slaposDrillHoles:
|
||||
return m_hollowing_data && !m_hollowing_data->hollow_mesh_with_holes.empty();
|
||||
return ! this->m_mesh_from_slices.empty();
|
||||
case slaposSupportTree:
|
||||
return ! this->support_mesh().empty();
|
||||
case slaposPad:
|
||||
@ -1015,12 +1021,16 @@ bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const
|
||||
TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
|
||||
{
|
||||
switch (step) {
|
||||
case slaposAssembly:
|
||||
return m_transformed_rmesh;
|
||||
case slaposObjectSlice:
|
||||
return this->m_mesh_from_slices;
|
||||
case slaposSupportTree:
|
||||
return this->support_mesh();
|
||||
case slaposPad:
|
||||
return this->pad_mesh();
|
||||
case slaposDrillHoles:
|
||||
if (m_hollowing_data)
|
||||
// if (m_hollowing_data)
|
||||
return get_mesh_to_print();
|
||||
[[fallthrough]];
|
||||
default:
|
||||
@ -1054,16 +1064,7 @@ const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const
|
||||
}
|
||||
|
||||
const TriangleMesh &SLAPrintObject::transformed_mesh() const {
|
||||
// we need to transform the raw mesh...
|
||||
// currently all the instances share the same x and y rotation and scaling
|
||||
// so we have to extract those from e.g. the first instance and apply to the
|
||||
// raw mesh. This is also true for the support points.
|
||||
// BUT: when the support structure is spawned for each instance than it has
|
||||
// to omit the X, Y rotation and scaling as those have been already applied
|
||||
// or apply an inverse transformation on the support structure after it
|
||||
// has been created.
|
||||
|
||||
return m_transformed_rmesh.get();
|
||||
return m_transformed_rmesh;
|
||||
}
|
||||
|
||||
sla::SupportPoints SLAPrintObject::transformed_support_points() const
|
||||
|
@ -3,16 +3,15 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "PrintBase.hpp"
|
||||
#include "SLA/RasterBase.hpp"
|
||||
#include "SLA/SupportTree.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include "Zipper.hpp"
|
||||
#include "Format/SLAArchiveWriter.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
|
||||
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
||||
#include "libslic3r/CSGMesh/CSGMesh.hpp"
|
||||
#include "libslic3r/OpenVDBUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -23,6 +22,7 @@ enum SLAPrintStep : unsigned int {
|
||||
};
|
||||
|
||||
enum SLAPrintObjectStep : unsigned int {
|
||||
slaposAssembly,
|
||||
slaposHollowing,
|
||||
slaposDrillHoles,
|
||||
slaposObjectSlice,
|
||||
@ -45,6 +45,53 @@ using _SLAPrintObjectBase =
|
||||
|
||||
enum SliceOrigin { soSupport, soModel };
|
||||
|
||||
// Each sla object step can hold a collection of csg operations on the
|
||||
// sla model to be sliced. Currently, Assembly step adds negative and positive
|
||||
// volumes, hollowing adds the negative interior, drilling adds the hole cylinders.
|
||||
// They need to be processed in this specific order. If CSGPartForStep instances
|
||||
// are put into a multiset container the key being the sla step,
|
||||
// iterating over the container will maintain the correct order of csg operations.
|
||||
struct CSGPartForStep : public csg::CSGPart
|
||||
{
|
||||
SLAPrintObjectStep key;
|
||||
mutable struct { VoxelGridPtr gridptr; csg::VoxelizeParams params; } cache;
|
||||
|
||||
CSGPartForStep(SLAPrintObjectStep k, CSGPart &&p = {})
|
||||
: key{k}, CSGPart{std::move(p)}
|
||||
{}
|
||||
|
||||
CSGPartForStep &operator=(CSGPart &&part)
|
||||
{
|
||||
this->its_ptr = std::move(part.its_ptr);
|
||||
this->operation = part.operation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const CSGPartForStep &other) const { return key < other.key; }
|
||||
};
|
||||
|
||||
namespace csg {
|
||||
|
||||
//inline VoxelGridPtr get_voxelgrid(const CSGPartForStep &part,
|
||||
// const VoxelizeParams &p)
|
||||
//{
|
||||
// if (!part.cache.gridptr || get_voxel_scale(*part.cache.gridptr) < p.voxel_scale()) {
|
||||
// part.cache.gridptr = mesh_to_grid(*csg::get_mesh(part), p.voxel_scale(),
|
||||
// p.exterior_bandwidth(),
|
||||
// p.interior_bandwidth());
|
||||
|
||||
// } /*else {
|
||||
// float vscale = p.voxel_scale();
|
||||
// float oscale = get_voxel_scale(*part.cache.gridptr);
|
||||
|
||||
// rescale_grid(*part.cache.gridptr, oscale/vscale);
|
||||
// }*/
|
||||
|
||||
// return clone(*part.cache.gridptr);
|
||||
//}
|
||||
|
||||
} // namespace csg
|
||||
|
||||
class SLAPrintObject : public _SLAPrintObjectBase
|
||||
{
|
||||
private: // Prevents erroneous use by other classes.
|
||||
@ -88,11 +135,7 @@ public:
|
||||
// Get the mesh that is going to be printed with all the modifications
|
||||
// like hollowing and drilled holes.
|
||||
const TriangleMesh & get_mesh_to_print() const {
|
||||
return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes_trimmed : transformed_mesh();
|
||||
}
|
||||
|
||||
const TriangleMesh & get_mesh_to_slice() const {
|
||||
return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh();
|
||||
return !m_mesh_from_slices.empty() ? m_mesh_from_slices : transformed_mesh();
|
||||
}
|
||||
|
||||
// This will return the transformed mesh which is cached
|
||||
@ -275,7 +318,8 @@ protected:
|
||||
{ m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||
|
||||
void set_trafo(const Transform3d& trafo, bool left_handed) {
|
||||
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
|
||||
m_trafo = trafo;
|
||||
m_left_handed = left_handed;
|
||||
}
|
||||
|
||||
template<class InstVec> inline void set_instances(InstVec&& instances) { m_instances = std::forward<InstVec>(instances); }
|
||||
@ -307,7 +351,7 @@ private:
|
||||
std::vector<float> m_model_height_levels;
|
||||
|
||||
// Caching the transformed (m_trafo) raw mesh of the object
|
||||
mutable CachedObject<TriangleMesh> m_transformed_rmesh;
|
||||
TriangleMesh m_transformed_rmesh;
|
||||
|
||||
struct SupportData
|
||||
{
|
||||
@ -333,16 +377,16 @@ private:
|
||||
pad_mesh = TriangleMesh{sla::create_pad(input, tree_mesh.its, ctl)};
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<SupportData> m_supportdata;
|
||||
|
||||
|
||||
std::unique_ptr<SupportData> m_supportdata;
|
||||
TriangleMesh m_mesh_from_slices;
|
||||
std::multiset<CSGPartForStep> m_mesh_to_slice;
|
||||
|
||||
class HollowingData
|
||||
{
|
||||
public:
|
||||
|
||||
sla::InteriorPtr interior;
|
||||
mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh
|
||||
mutable TriangleMesh hollow_mesh_with_holes_trimmed;
|
||||
};
|
||||
|
||||
std::unique_ptr<HollowingData> m_hollowing_data;
|
||||
|
@ -14,6 +14,12 @@
|
||||
|
||||
#include <libslic3r/ElephantFootCompensation.hpp>
|
||||
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||
#include <libslic3r/MeshSplitImpl.hpp>
|
||||
#include <libslic3r/SlicesToTriangleMesh.hpp>
|
||||
#include <libslic3r/CSGMesh/ModelToCSGMesh.hpp>
|
||||
#include <libslic3r/CSGMesh/SliceCSGMesh.hpp>
|
||||
#include <libslic3r/OpenVDBUtils.hpp>
|
||||
#include <libslic3r/QuadricEdgeCollapse.hpp>
|
||||
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/QuadricEdgeCollapse.hpp>
|
||||
@ -33,7 +39,8 @@ namespace Slic3r {
|
||||
namespace {
|
||||
|
||||
const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = {
|
||||
10, // slaposHollowing,
|
||||
5, // slaposAssembly
|
||||
5, // slaposHollowing,
|
||||
10, // slaposDrillHoles
|
||||
10, // slaposObjectSlice,
|
||||
20, // slaposSupportPoints,
|
||||
@ -45,6 +52,7 @@ const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = {
|
||||
std::string OBJ_STEP_LABELS(size_t idx)
|
||||
{
|
||||
switch (idx) {
|
||||
case slaposAssembly: return L("Assembling model from parts");
|
||||
case slaposHollowing: return L("Hollowing model");
|
||||
case slaposDrillHoles: return L("Drilling holes into model.");
|
||||
case slaposObjectSlice: return L("Slicing model");
|
||||
@ -120,9 +128,38 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin
|
||||
}
|
||||
}
|
||||
|
||||
void clear_csg(std::multiset<CSGPartForStep> &s, SLAPrintObjectStep step)
|
||||
{
|
||||
auto r = s.equal_range(step);
|
||||
s.erase(r.first, r.second);
|
||||
}
|
||||
|
||||
struct csg_inserter {
|
||||
std::multiset<CSGPartForStep> &m;
|
||||
SLAPrintObjectStep key;
|
||||
|
||||
csg_inserter &operator*() { return *this; }
|
||||
void operator=(csg::CSGPart &&part) { m.emplace(key, std::move(part)); }
|
||||
csg_inserter& operator++() { return *this; }
|
||||
};
|
||||
|
||||
void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po)
|
||||
{
|
||||
po.m_mesh_to_slice.clear();
|
||||
|
||||
po.m_transformed_rmesh = po.m_model_object->raw_mesh();
|
||||
po.m_transformed_rmesh.transform(po.trafo());
|
||||
|
||||
csg::model_to_csgmesh(*po.model_object(), po.trafo(),
|
||||
csg_inserter{po.m_mesh_to_slice, slaposAssembly},
|
||||
csg::mpartsPositive | csg::mpartsNegative);
|
||||
}
|
||||
|
||||
void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
||||
{
|
||||
po.m_hollowing_data.reset();
|
||||
clear_csg(po.m_mesh_to_slice, slaposDrillHoles);
|
||||
clear_csg(po.m_mesh_to_slice, slaposHollowing);
|
||||
|
||||
if (! po.m_config.hollowing_enable.getBool()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!";
|
||||
@ -139,7 +176,12 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
||||
ctl.stopcondition = [this]() { return canceled(); };
|
||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
sla::InteriorPtr interior = generate_interior(po.transformed_mesh().its, hlwcfg, ctl);
|
||||
sla::JobController ctl;
|
||||
ctl.stopcondition = [this]() { return canceled(); };
|
||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
sla::InteriorPtr interior =
|
||||
generate_interior(range(po.m_mesh_to_slice), hlwcfg, ctl);
|
||||
|
||||
if (!interior || sla::get_mesh(*interior).empty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
|
||||
@ -147,166 +189,74 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
po.m_hollowing_data->interior = std::move(interior);
|
||||
|
||||
indexed_triangle_set &m = sla::get_mesh(*po.m_hollowing_data->interior);
|
||||
|
||||
if (!m.empty()) {
|
||||
// simplify mesh lossless
|
||||
float loss_less_max_error = 2*std::numeric_limits<float>::epsilon();
|
||||
its_quadric_edge_collapse(m, 0U, &loss_less_max_error);
|
||||
|
||||
its_compactify_vertices(m);
|
||||
its_merge_vertices(m);
|
||||
|
||||
// flip normals back...
|
||||
sla::swap_normals(m);
|
||||
}
|
||||
// Put the interior into the target mesh as a negative
|
||||
po.m_mesh_to_slice
|
||||
.emplace(slaposHollowing,
|
||||
csg::CSGPart{&sla::get_mesh(*po.m_hollowing_data->interior),
|
||||
csg::CSGType::Difference});
|
||||
}
|
||||
}
|
||||
|
||||
static indexed_triangle_set
|
||||
remove_unconnected_vertices(const indexed_triangle_set &its)
|
||||
{
|
||||
if (its.indices.empty()) {};
|
||||
|
||||
indexed_triangle_set M;
|
||||
its_compactify_vertices(M);
|
||||
|
||||
return M;
|
||||
}
|
||||
|
||||
// Drill holes into the hollowed/original mesh.
|
||||
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
{
|
||||
bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty();
|
||||
bool is_hollowed =
|
||||
(po.m_hollowing_data && po.m_hollowing_data->interior &&
|
||||
!sla::get_mesh(*po.m_hollowing_data->interior).empty());
|
||||
clear_csg(po.m_mesh_to_slice, slaposDrillHoles);
|
||||
|
||||
if (! is_hollowed && ! needs_drilling) {
|
||||
// In this case we can dump any data that might have been
|
||||
// generated on previous runs.
|
||||
po.m_hollowing_data.reset();
|
||||
return;
|
||||
}
|
||||
csg::model_to_csgmesh(*po.model_object(), po.trafo(),
|
||||
csg_inserter{po.m_mesh_to_slice, slaposDrillHoles},
|
||||
csg::mpartsDrillHoles);
|
||||
|
||||
if (! po.m_hollowing_data)
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
// update preview mesh
|
||||
double vscale = 1. / 0.05;
|
||||
auto voxparams = csg::VoxelizeParams{}
|
||||
.voxel_scale(vscale)
|
||||
.exterior_bandwidth(1.f)
|
||||
.interior_bandwidth(1.f);
|
||||
auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams);
|
||||
indexed_triangle_set mesh_from_slices = grid_to_mesh(*grid);
|
||||
po.m_mesh_from_slices = TriangleMesh{mesh_from_slices};
|
||||
}
|
||||
|
||||
// Hollowing and/or drilling is active, m_hollowing_data is valid.
|
||||
|
||||
// Regenerate hollowed mesh, even if it was there already. It may contain
|
||||
// holes that are no longer on the frontend.
|
||||
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||
hollowed_mesh = po.transformed_mesh();
|
||||
if (is_hollowed)
|
||||
sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior);
|
||||
|
||||
TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed;
|
||||
|
||||
if (! needs_drilling) {
|
||||
mesh_view = po.transformed_mesh();
|
||||
|
||||
if (is_hollowed)
|
||||
sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior,
|
||||
sla::hfRemoveInsideTriangles);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
|
||||
sla::DrainHoles drainholes = po.transformed_drainhole_points();
|
||||
|
||||
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
hollowed_mesh.its.vertices,
|
||||
hollowed_mesh.its.indices
|
||||
);
|
||||
|
||||
std::uniform_real_distribution<float> dist(0., float(EPSILON));
|
||||
auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {});
|
||||
indexed_triangle_set part_to_drill = hollowed_mesh.its;
|
||||
|
||||
bool hole_fail = false;
|
||||
for (size_t i = 0; i < drainholes.size(); ++i) {
|
||||
sla::DrainHole holept = drainholes[i];
|
||||
|
||||
holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)};
|
||||
holept.normal.normalize();
|
||||
holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)};
|
||||
indexed_triangle_set m = holept.to_mesh();
|
||||
|
||||
part_to_drill.indices.clear();
|
||||
auto bb = bounding_box(m);
|
||||
Eigen::AlignedBox<float, 3> ebb{bb.min.cast<float>(),
|
||||
bb.max.cast<float>()};
|
||||
|
||||
AABBTreeIndirect::traverse(
|
||||
tree,
|
||||
AABBTreeIndirect::intersecting(ebb),
|
||||
[&part_to_drill, &hollowed_mesh](const auto& node)
|
||||
{
|
||||
part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[node.idx]);
|
||||
});
|
||||
|
||||
auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(
|
||||
remove_unconnected_vertices(part_to_drill));
|
||||
|
||||
if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to drill hole";
|
||||
|
||||
hole_fail = drainholes[i].failed =
|
||||
po.model_object()->sla_drain_holes[i].failed = true;
|
||||
|
||||
continue;
|
||||
template<class Pred>
|
||||
static std::vector<ExPolygons> slice_volumes(
|
||||
const ModelVolumePtrs &volumes,
|
||||
const std::vector<float> &slice_grid,
|
||||
const Transform3d &trafo,
|
||||
const MeshSlicingParamsEx &slice_params,
|
||||
Pred &&predicate)
|
||||
{
|
||||
indexed_triangle_set mesh;
|
||||
for (const ModelVolume *vol : volumes) {
|
||||
if (predicate(vol)) {
|
||||
indexed_triangle_set vol_mesh = vol->mesh().its;
|
||||
its_transform(vol_mesh, trafo * vol->get_matrix());
|
||||
its_merge(mesh, vol_mesh);
|
||||
}
|
||||
|
||||
auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
|
||||
MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole);
|
||||
}
|
||||
|
||||
if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal))
|
||||
throw Slic3r::SlicingError(L("Too many overlapping holes."));
|
||||
std::vector<ExPolygons> out;
|
||||
|
||||
auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh);
|
||||
|
||||
if (!MeshBoolean::cgal::does_bound_a_volume(*hollowed_mesh_cgal)) {
|
||||
po.active_step_add_warning(
|
||||
PrintStateBase::WarningLevel::NON_CRITICAL,
|
||||
L("Mesh to be hollowed is not suitable for hollowing (does not "
|
||||
"bound a volume)."));
|
||||
if (!mesh.empty()) {
|
||||
out = slice_mesh_ex(mesh, slice_grid, slice_params);
|
||||
}
|
||||
|
||||
if (!MeshBoolean::cgal::empty(*holes_mesh_cgal)
|
||||
&& !MeshBoolean::cgal::does_bound_a_volume(*holes_mesh_cgal)) {
|
||||
po.active_step_add_warning(
|
||||
PrintStateBase::WarningLevel::NON_CRITICAL,
|
||||
L("Unable to drill the current configuration of holes into the "
|
||||
"model."));
|
||||
return out;
|
||||
}
|
||||
|
||||
template<class Cont> BoundingBoxf3 csgmesh_positive_bb(const Cont &csg)
|
||||
{
|
||||
// Calculate the biggest possible bounding box of the mesh to be sliced
|
||||
// from all the positive parts that it contains.
|
||||
auto bb3d = csg.empty() ? BoundingBoxf3{} :
|
||||
bounding_box(*csg::get_mesh(*csg.begin()),
|
||||
csg::get_transform(*csg.begin()));
|
||||
|
||||
for (const auto &m : csg) {
|
||||
if (m.operation == csg::CSGType::Union)
|
||||
bb3d.merge(bounding_box(*csg::get_mesh(m), csg::get_transform(m)));
|
||||
}
|
||||
|
||||
try {
|
||||
if (!MeshBoolean::cgal::empty(*holes_mesh_cgal))
|
||||
MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal);
|
||||
|
||||
hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
|
||||
mesh_view = hollowed_mesh;
|
||||
|
||||
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 Slic3r::RuntimeError &) {
|
||||
throw Slic3r::SlicingError(L(
|
||||
"Drilling holes into the mesh failed. "
|
||||
"This is usually caused by broken model. Try to fix it first."));
|
||||
}
|
||||
|
||||
if (hole_fail)
|
||||
po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
|
||||
L("Failed to drill some holes into the model"));
|
||||
return bb3d;
|
||||
}
|
||||
|
||||
// The slicing will be performed on an imaginary 1D grid which starts from
|
||||
@ -319,14 +269,17 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
// same imaginary grid (the height vector argument to TriangleMeshSlicer).
|
||||
void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
||||
{
|
||||
const TriangleMesh &mesh = po.get_mesh_to_slice();
|
||||
// The first mesh in the csg sequence is assumed to be a positive part
|
||||
assert(po.m_mesh_to_slice.empty() ||
|
||||
csg::get_operation(*po.m_mesh_to_slice.begin()) == csg::CSGType::Union);
|
||||
|
||||
auto bb3d = csgmesh_positive_bb(po.m_mesh_to_slice);
|
||||
|
||||
// We need to prepare the slice index...
|
||||
|
||||
double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat();
|
||||
float lh = float(lhd);
|
||||
coord_t lhs = scaled(lhd);
|
||||
auto && bb3d = mesh.bounding_box();
|
||||
double minZ = bb3d.min(Z) - po.get_elevation();
|
||||
double maxZ = bb3d.max(Z);
|
||||
auto minZf = float(minZ);
|
||||
@ -368,27 +321,9 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
||||
}
|
||||
auto thr = [this]() { m_print->throw_if_canceled(); };
|
||||
auto &slice_grid = po.m_model_height_levels;
|
||||
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr);
|
||||
|
||||
sla::Interior *interior = po.m_hollowing_data ?
|
||||
po.m_hollowing_data->interior.get() :
|
||||
nullptr;
|
||||
|
||||
if (interior && ! sla::get_mesh(*interior).empty()) {
|
||||
indexed_triangle_set interiormesh = sla::get_mesh(*interior);
|
||||
sla::swap_normals(interiormesh);
|
||||
params.mode = MeshSlicingParams::SlicingMode::Regular;
|
||||
|
||||
std::vector<ExPolygons> interior_slices = slice_mesh_ex(interiormesh, slice_grid, params, thr);
|
||||
|
||||
execution::for_each(
|
||||
ex_tbb, size_t(0), interior_slices.size(),
|
||||
[&po, &interior_slices](size_t i) {
|
||||
const ExPolygons &slice = interior_slices[i];
|
||||
po.m_model_slices[i] = diff_ex(po.m_model_slices[i], slice);
|
||||
},
|
||||
execution::max_concurrency(ex_tbb));
|
||||
}
|
||||
Range csgrange = {po.m_mesh_to_slice.begin(), po.m_mesh_to_slice.end()};
|
||||
po.m_model_slices = slice_csgmesh_ex(csgrange, slice_grid, params, thr);
|
||||
|
||||
auto mit = slindex_it;
|
||||
for (size_t id = 0;
|
||||
@ -400,10 +335,86 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
||||
// We apply the printer correction offset here.
|
||||
apply_printer_corrections(po, soModel);
|
||||
|
||||
if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool())
|
||||
{
|
||||
po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print()));
|
||||
// auto simpl_slices = reserve_vector<ExPolygons>(po.m_model_slices.size());
|
||||
// for (const ExPolygons &slice : po.m_model_slices) {
|
||||
// simpl_slices.emplace_back(expolygons_simplify(slice, scaled(1e-2)));
|
||||
// }
|
||||
|
||||
// po.m_mesh_from_slices = TriangleMesh{
|
||||
// slices_to_mesh(simpl_slices, slice_grid.front(), lhd, ilhd)};
|
||||
|
||||
double vscale = 1. / lhd;
|
||||
auto voxparams = csg::VoxelizeParams{}
|
||||
.voxel_scale(vscale)
|
||||
.exterior_bandwidth(1.f)
|
||||
.interior_bandwidth(1.f);
|
||||
auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams);
|
||||
|
||||
assert(grid);
|
||||
|
||||
// size_t max_face_cnt = 0;
|
||||
// for (const CSGMesh &part : po.m_mesh_to_slice)
|
||||
// max_face_cnt += part.its_ptr->indices.size();
|
||||
|
||||
indexed_triangle_set mesh_from_slices = grid_to_mesh(*grid);
|
||||
|
||||
// its_quadric_edge_collapse(mesh_from_slices, vscale * max_face_cnt);
|
||||
|
||||
// its_compactify_vertices(mesh_from_slices);
|
||||
// its_merge_vertices(mesh_from_slices);
|
||||
|
||||
po.m_mesh_from_slices = TriangleMesh{mesh_from_slices};
|
||||
|
||||
// po.m_mesh_from_slices = TriangleMesh{sla::get_mesh(*po.m_hollowing_data->interior)};
|
||||
|
||||
}
|
||||
|
||||
static void filter_support_points_by_modifiers(
|
||||
sla::SupportPoints &pts,
|
||||
const std::vector<ExPolygons> &blockers,
|
||||
const std::vector<ExPolygons> &enforcers,
|
||||
const std::vector<float> &slice_grid)
|
||||
{
|
||||
assert((blockers.empty() || blockers.size() == slice_grid.size()) &&
|
||||
(enforcers.empty() || enforcers.size() == slice_grid.size()));
|
||||
|
||||
auto new_pts = reserve_vector<sla::SupportPoint>(pts.size());
|
||||
|
||||
for (size_t i = 0; i < pts.size(); ++i) {
|
||||
const sla::SupportPoint &sp = pts[i];
|
||||
Point sp2d = scaled(to_2d(sp.pos));
|
||||
|
||||
auto it = std::lower_bound(slice_grid.begin(), slice_grid.end(), sp.pos.z());
|
||||
if (it != slice_grid.end()) {
|
||||
size_t idx = std::distance(slice_grid.begin(), it);
|
||||
bool is_enforced = false;
|
||||
if (idx < enforcers.size()) {
|
||||
for (size_t enf_idx = 0;
|
||||
!is_enforced && enf_idx < enforcers[idx].size();
|
||||
++enf_idx)
|
||||
{
|
||||
if (enforcers[idx][enf_idx].contains_b(sp2d))
|
||||
is_enforced = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_blocked = false;
|
||||
if (!is_enforced && idx < blockers.size()) {
|
||||
for (size_t blk_idx = 0;
|
||||
!is_blocked && blk_idx < blockers[idx].size();
|
||||
++blk_idx)
|
||||
{
|
||||
if (blockers[idx][blk_idx].contains_b(sp2d))
|
||||
is_blocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_blocked)
|
||||
new_pts.emplace_back(sp);
|
||||
}
|
||||
}
|
||||
|
||||
pts.swap(new_pts);
|
||||
}
|
||||
|
||||
// In this step we check the slices, identify island and cover them with
|
||||
@ -414,9 +425,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
if(!po.m_config.supports_enable.getBool()) return;
|
||||
|
||||
if (!po.m_supportdata)
|
||||
po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print()));
|
||||
po.m_supportdata.reset(new SLAPrintObject::SupportData(po.m_mesh_from_slices));
|
||||
|
||||
po.m_supportdata->input.zoffset = bounding_box(po.get_mesh_to_print())
|
||||
po.m_supportdata->input.zoffset = csgmesh_positive_bb(po.m_mesh_to_slice)
|
||||
.min.z();
|
||||
|
||||
const ModelObject& mo = *po.m_model_object;
|
||||
@ -432,11 +443,6 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
// calculate heights of slices (slices are calculated already)
|
||||
const std::vector<float>& heights = po.m_model_height_levels;
|
||||
|
||||
// Tell the mesh where drain holes are. Although the points are
|
||||
// calculated on slices, the algorithm then raycasts the points
|
||||
// so they actually lie on the mesh.
|
||||
// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points());
|
||||
|
||||
throw_if_canceled();
|
||||
sla::SupportPointGenerator::Config config;
|
||||
const SLAPrintObjectConfig& cfg = po.config();
|
||||
@ -464,8 +470,27 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
heights, config, [this]() { throw_if_canceled(); }, statuscb);
|
||||
|
||||
// Now let's extract the result.
|
||||
const std::vector<sla::SupportPoint>& points = auto_supports.output();
|
||||
std::vector<sla::SupportPoint>& points = auto_supports.output();
|
||||
throw_if_canceled();
|
||||
|
||||
MeshSlicingParamsEx params;
|
||||
params.closing_radius = float(po.config().slice_closing_radius.value);
|
||||
std::vector<ExPolygons> blockers =
|
||||
slice_volumes(po.model_object()->volumes,
|
||||
po.m_model_height_levels, po.trafo(), params,
|
||||
[](const ModelVolume *vol) {
|
||||
return vol->is_support_blocker();
|
||||
});
|
||||
|
||||
std::vector<ExPolygons> enforcers =
|
||||
slice_volumes(po.model_object()->volumes,
|
||||
po.m_model_height_levels, po.trafo(), params,
|
||||
[](const ModelVolume *vol) {
|
||||
return vol->is_support_enforcer();
|
||||
});
|
||||
|
||||
filter_support_points_by_modifiers(points, blockers, enforcers, po.m_model_height_levels);
|
||||
|
||||
po.m_supportdata->input.pts = points;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
@ -487,11 +512,6 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
|
||||
{
|
||||
if(!po.m_supportdata) return;
|
||||
|
||||
// sla::PadConfig pcfg = make_pad_cfg(po.m_config);
|
||||
|
||||
// if (pcfg.embed_object)
|
||||
// po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm);
|
||||
|
||||
// If the zero elevation mode is engaged, we have to filter out all the
|
||||
// points that are on the bottom of the object
|
||||
if (is_zero_elevation(po.config())) {
|
||||
@ -503,7 +523,6 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
|
||||
|
||||
po.m_supportdata->input.cfg = make_support_cfg(po.m_config);
|
||||
po.m_supportdata->input.pad_cfg = make_pad_cfg(po.m_config);
|
||||
// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points());
|
||||
|
||||
// scaling for the sub operations
|
||||
double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0;
|
||||
@ -554,16 +573,6 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
|
||||
sla::PadConfig pcfg = make_pad_cfg(po.m_config);
|
||||
po.m_supportdata->input.pad_cfg = pcfg;
|
||||
|
||||
// if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
|
||||
// // No support (thus no elevation) or zero elevation mode
|
||||
// // we sometimes call it "builtin pad" is enabled so we will
|
||||
// // get a sample from the bottom of the mesh and use it for pad
|
||||
// // creation.
|
||||
// sla::pad_blueprint(trmesh.its, bp, float(pad_h),
|
||||
// float(po.m_config.layer_height.getFloat()),
|
||||
// [this](){ throw_if_canceled(); });
|
||||
// }
|
||||
|
||||
sla::JobController ctl;
|
||||
ctl.stopcondition = [this]() { return canceled(); };
|
||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
@ -996,6 +1005,7 @@ double SLAPrint::Steps::progressrange(SLAPrintStep step) const
|
||||
void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj)
|
||||
{
|
||||
switch(step) {
|
||||
case slaposAssembly: mesh_assembly(obj); break;
|
||||
case slaposHollowing: hollow_model(obj); break;
|
||||
case slaposDrillHoles: drill_holes(obj); break;
|
||||
case slaposObjectSlice: slice_model(obj); break;
|
||||
|
@ -48,6 +48,7 @@ private:
|
||||
public:
|
||||
explicit Steps(SLAPrint *print);
|
||||
|
||||
void mesh_assembly(SLAPrintObject &po);
|
||||
void hollow_model(SLAPrintObject &po);
|
||||
void drill_holes (SLAPrintObject &po);
|
||||
void slice_model(SLAPrintObject& po);
|
||||
|
Loading…
x
Reference in New Issue
Block a user