Add support for csg operation stacking
This commit is contained in:
parent
4fabd0b307
commit
25ca46e3eb
6 changed files with 185 additions and 49 deletions
|
@ -18,6 +18,7 @@ namespace Slic3r { namespace csg {
|
||||||
|
|
||||||
// Supported CSG operation types
|
// Supported CSG operation types
|
||||||
enum class CSGType { Union, Difference, Intersection };
|
enum class CSGType { Union, Difference, Intersection };
|
||||||
|
enum class CSGStackOp { Push, Continue, Pop };
|
||||||
|
|
||||||
// Get the CSG operation of the part. Can be overriden for any type
|
// Get the CSG operation of the part. Can be overriden for any type
|
||||||
template<class CSGPartT> CSGType get_operation(const CSGPartT &part)
|
template<class CSGPartT> CSGType get_operation(const CSGPartT &part)
|
||||||
|
@ -25,6 +26,11 @@ template<class CSGPartT> CSGType get_operation(const CSGPartT &part)
|
||||||
return part.operation;
|
return part.operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class CSGPartT> CSGStackOp get_stack_operation(const CSGPartT &part)
|
||||||
|
{
|
||||||
|
return part.stack_operation;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the mesh for the part. Can be overriden for any type
|
// Get the mesh for the part. Can be overriden for any type
|
||||||
template<class CSGPartT>
|
template<class CSGPartT>
|
||||||
const indexed_triangle_set *get_mesh(const CSGPartT &part)
|
const indexed_triangle_set *get_mesh(const CSGPartT &part)
|
||||||
|
@ -48,6 +54,11 @@ inline CSGType get_operation(const indexed_triangle_set &part)
|
||||||
return CSGType::Union;
|
return CSGType::Union;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline CSGStackOp get_stack_operation(const indexed_triangle_set &part)
|
||||||
|
{
|
||||||
|
return CSGStackOp::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
inline const indexed_triangle_set * get_mesh(const indexed_triangle_set &part)
|
inline const indexed_triangle_set * get_mesh(const indexed_triangle_set &part)
|
||||||
{
|
{
|
||||||
return ∂
|
return ∂
|
||||||
|
@ -63,6 +74,7 @@ struct CSGPart {
|
||||||
AnyPtr<const indexed_triangle_set> its_ptr;
|
AnyPtr<const indexed_triangle_set> its_ptr;
|
||||||
Transform3f trafo;
|
Transform3f trafo;
|
||||||
CSGType operation;
|
CSGType operation;
|
||||||
|
CSGStackOp stack_operation;
|
||||||
|
|
||||||
CSGPart(AnyPtr<const indexed_triangle_set> ptr = {},
|
CSGPart(AnyPtr<const indexed_triangle_set> ptr = {},
|
||||||
CSGType op = CSGType::Union,
|
CSGType op = CSGType::Union,
|
||||||
|
|
|
@ -5,13 +5,15 @@
|
||||||
|
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/SLA/Hollowing.hpp"
|
#include "libslic3r/SLA/Hollowing.hpp"
|
||||||
|
#include "libslic3r/MeshSplitImpl.hpp"
|
||||||
|
|
||||||
namespace Slic3r { namespace csg {
|
namespace Slic3r { namespace csg {
|
||||||
|
|
||||||
enum ModelParts {
|
enum ModelParts {
|
||||||
mpartsPositive = 1,
|
mpartsPositive = 1,
|
||||||
mpartsNegative = 2,
|
mpartsNegative = 2,
|
||||||
mpartsDrillHoles = 4
|
mpartsDrillHoles = 4,
|
||||||
|
mpartsDoSplits = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class OutIt>
|
template<class OutIt>
|
||||||
|
@ -25,11 +27,33 @@ void model_to_csgmesh(const ModelObject &mo,
|
||||||
bool do_positives = parts_to_include & mpartsPositive;
|
bool do_positives = parts_to_include & mpartsPositive;
|
||||||
bool do_negatives = parts_to_include & mpartsNegative;
|
bool do_negatives = parts_to_include & mpartsNegative;
|
||||||
bool do_drillholes = parts_to_include & mpartsDrillHoles;
|
bool do_drillholes = parts_to_include & mpartsDrillHoles;
|
||||||
|
bool do_splits = parts_to_include & mpartsDoSplits;
|
||||||
|
|
||||||
for (const ModelVolume *vol : mo.volumes) {
|
for (const ModelVolume *vol : mo.volumes) {
|
||||||
if (vol && vol->mesh_ptr() &&
|
if (vol && vol->mesh_ptr() &&
|
||||||
((do_positives && vol->is_model_part()) ||
|
((do_positives && vol->is_model_part()) ||
|
||||||
(do_negatives && vol->is_negative_volume()))) {
|
(do_negatives && vol->is_negative_volume()))) {
|
||||||
|
|
||||||
|
if (do_splits && its_is_splittable(vol->mesh().its)) {
|
||||||
|
CSGPart part_begin{{}, vol->is_model_part() ? CSGType::Union : CSGType::Difference};
|
||||||
|
part_begin.stack_operation = CSGStackOp::Push;
|
||||||
|
*out = std::move(part_begin);
|
||||||
|
++out;
|
||||||
|
|
||||||
|
its_split(vol->mesh().its, SplitOutputFn{[&out, &vol, &trafo](indexed_triangle_set &&its) {
|
||||||
|
CSGPart part{std::make_unique<indexed_triangle_set>(std::move(its)),
|
||||||
|
CSGType::Union,
|
||||||
|
(trafo * vol->get_matrix()).cast<float>()};
|
||||||
|
|
||||||
|
*out = std::move(part);
|
||||||
|
++out;
|
||||||
|
}});
|
||||||
|
|
||||||
|
CSGPart part_end{{}};
|
||||||
|
part_end.stack_operation = CSGStackOp::Pop;
|
||||||
|
*out = std::move(part_end);
|
||||||
|
++out;
|
||||||
|
} else {
|
||||||
CSGPart part{&(vol->mesh().its),
|
CSGPart part{&(vol->mesh().its),
|
||||||
vol->is_model_part() ? CSGType::Union : CSGType::Difference,
|
vol->is_model_part() ? CSGType::Union : CSGType::Difference,
|
||||||
(trafo * vol->get_matrix()).cast<float>()};
|
(trafo * vol->get_matrix()).cast<float>()};
|
||||||
|
@ -38,6 +62,7 @@ void model_to_csgmesh(const ModelObject &mo,
|
||||||
++out;
|
++out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (do_drillholes) {
|
if (do_drillholes) {
|
||||||
sla::DrainHoles drainholes = sla::transformed_drainhole_points(mo, trafo);
|
sla::DrainHoles drainholes = sla::transformed_drainhole_points(mo, trafo);
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#define SLICECSGMESH_HPP
|
#define SLICECSGMESH_HPP
|
||||||
|
|
||||||
#include "CSGMesh.hpp"
|
#include "CSGMesh.hpp"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
#include "libslic3r/TriangleMeshSlicer.hpp"
|
#include "libslic3r/TriangleMeshSlicer.hpp"
|
||||||
#include "libslic3r/ClipperUtils.hpp"
|
#include "libslic3r/ClipperUtils.hpp"
|
||||||
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
||||||
|
@ -10,23 +13,36 @@ namespace Slic3r { namespace csg {
|
||||||
|
|
||||||
template<class ItCSG>
|
template<class ItCSG>
|
||||||
std::vector<ExPolygons> slice_csgmesh_ex(
|
std::vector<ExPolygons> slice_csgmesh_ex(
|
||||||
const Range<ItCSG> &csg,
|
const Range<ItCSG> &csgrange,
|
||||||
const std::vector<float> &slicegrid,
|
const std::vector<float> &slicegrid,
|
||||||
const MeshSlicingParamsEx ¶ms,
|
const MeshSlicingParamsEx ¶ms,
|
||||||
const std::function<void()> &throw_on_cancel = [] {})
|
const std::function<void()> &throw_on_cancel = [] {})
|
||||||
{
|
{
|
||||||
std::vector<ExPolygons> ret(slicegrid.size());
|
struct Frame { CSGType op; std::vector<ExPolygons> slices; };
|
||||||
|
|
||||||
|
std::stack opstack{std::vector<Frame>{}};
|
||||||
|
|
||||||
MeshSlicingParamsEx params_cpy = params;
|
MeshSlicingParamsEx params_cpy = params;
|
||||||
auto trafo = params.trafo;
|
auto trafo = params.trafo;
|
||||||
auto nonempty_indices = reserve_vector<size_t>(slicegrid.size());
|
auto nonempty_indices = reserve_vector<size_t>(slicegrid.size());
|
||||||
|
|
||||||
for (const auto &m : csg) {
|
if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
|
||||||
const indexed_triangle_set *its = csg::get_mesh(m);
|
opstack.push({CSGType::Union, std::vector<ExPolygons>(slicegrid.size())});
|
||||||
if (!its)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
params_cpy.trafo = trafo * csg::get_transform(m).template cast<double>();
|
for (const auto &csgpart : csgrange) {
|
||||||
|
const indexed_triangle_set *its = csg::get_mesh(csgpart);
|
||||||
|
|
||||||
|
auto op = get_operation(csgpart);
|
||||||
|
|
||||||
|
if (get_stack_operation(csgpart) == CSGStackOp::Push) {
|
||||||
|
opstack.push({op, std::vector<ExPolygons>(slicegrid.size())});
|
||||||
|
op = CSGType::Union;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame *top = &opstack.top();
|
||||||
|
|
||||||
|
if (its) {
|
||||||
|
params_cpy.trafo = trafo * csg::get_transform(csgpart).template cast<double>();
|
||||||
std::vector<ExPolygons> slices = slice_mesh_ex(*its,
|
std::vector<ExPolygons> slices = slice_mesh_ex(*its,
|
||||||
slicegrid, params_cpy,
|
slicegrid, params_cpy,
|
||||||
throw_on_cancel);
|
throw_on_cancel);
|
||||||
|
@ -35,22 +51,22 @@ std::vector<ExPolygons> slice_csgmesh_ex(
|
||||||
|
|
||||||
nonempty_indices.clear();
|
nonempty_indices.clear();
|
||||||
for (size_t i = 0; i < slicegrid.size(); ++i) {
|
for (size_t i = 0; i < slicegrid.size(); ++i) {
|
||||||
if (get_operation(m) == CSGType::Intersection || !slices[i].empty())
|
if (op == CSGType::Intersection || !slices[i].empty())
|
||||||
nonempty_indices.emplace_back(i);
|
nonempty_indices.emplace_back(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mergefn = [&m, &slices, &ret](size_t i){
|
auto mergefn = [&csgpart, &slices, &top](size_t i){
|
||||||
switch(get_operation(m)) {
|
switch(get_operation(csgpart)) {
|
||||||
case CSGType::Union:
|
case CSGType::Union:
|
||||||
for (ExPolygon &expoly : slices[i])
|
for (ExPolygon &expoly : slices[i])
|
||||||
ret[i].emplace_back(std::move(expoly));
|
top->slices[i].emplace_back(std::move(expoly));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case CSGType::Difference:
|
case CSGType::Difference:
|
||||||
ret[i] = diff_ex(ret[i], slices[i]);
|
top->slices[i] = diff_ex(top->slices[i], slices[i]);
|
||||||
break;
|
break;
|
||||||
case CSGType::Intersection:
|
case CSGType::Intersection:
|
||||||
ret[i] = intersection_ex(ret[i], slices[i]);
|
top->slices[i] = intersection_ex(top->slices[i], slices[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -61,6 +77,43 @@ std::vector<ExPolygons> slice_csgmesh_ex(
|
||||||
execution::max_concurrency(ex_tbb));
|
execution::max_concurrency(ex_tbb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_stack_operation(csgpart) == CSGStackOp::Pop) {
|
||||||
|
std::vector<ExPolygons> popslices = std::move(top->slices);
|
||||||
|
auto popop = opstack.top().op;
|
||||||
|
opstack.pop();
|
||||||
|
std::vector<ExPolygons> &prev_slices = opstack.top().slices;
|
||||||
|
|
||||||
|
nonempty_indices.clear();
|
||||||
|
for (size_t i = 0; i < slicegrid.size(); ++i) {
|
||||||
|
if (popop == CSGType::Intersection || !popslices[i].empty())
|
||||||
|
nonempty_indices.emplace_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mergefn2 = [&popslices, &prev_slices, popop](size_t i){
|
||||||
|
switch(popop) {
|
||||||
|
case CSGType::Union:
|
||||||
|
for (ExPolygon &expoly : popslices[i])
|
||||||
|
prev_slices[i].emplace_back(std::move(expoly));
|
||||||
|
|
||||||
|
break;
|
||||||
|
case CSGType::Difference:
|
||||||
|
prev_slices[i] = diff_ex(prev_slices[i], popslices[i]);
|
||||||
|
break;
|
||||||
|
case CSGType::Intersection:
|
||||||
|
prev_slices[i] = intersection_ex(prev_slices[i], popslices[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
execution::for_each(ex_tbb,
|
||||||
|
nonempty_indices.begin(), nonempty_indices.end(),
|
||||||
|
mergefn2,
|
||||||
|
execution::max_concurrency(ex_tbb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ExPolygons> ret = std::move(opstack.top().slices);
|
||||||
|
|
||||||
execution::for_each(ex_tbb, ret.begin(), ret.end(), [](ExPolygons &slice) {
|
execution::for_each(ex_tbb, ret.begin(), ret.end(), [](ExPolygons &slice) {
|
||||||
auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){
|
auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){
|
||||||
return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON);
|
return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define VOXELIZECSGMESH_HPP
|
#define VOXELIZECSGMESH_HPP
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
#include "CSGMesh.hpp"
|
#include "CSGMesh.hpp"
|
||||||
#include "libslic3r/OpenVDBUtils.hpp"
|
#include "libslic3r/OpenVDBUtils.hpp"
|
||||||
|
@ -46,29 +47,65 @@ VoxelGridPtr voxelize_csgmesh(const Range<It> &csgrange,
|
||||||
}, execution::max_concurrency(ex_tbb));
|
}, execution::max_concurrency(ex_tbb));
|
||||||
|
|
||||||
size_t csgidx = 0;
|
size_t csgidx = 0;
|
||||||
|
struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; };
|
||||||
|
std::stack opstack{std::vector<Frame>{}};
|
||||||
|
|
||||||
|
if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
|
||||||
|
opstack.push({});
|
||||||
|
|
||||||
for (auto &csgpart : csgrange) {
|
for (auto &csgpart : csgrange) {
|
||||||
if (params.statusfn() && params.statusfn()(-1))
|
if (params.statusfn() && params.statusfn()(-1))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto &partgrid = grids[csgidx++];
|
auto &partgrid = grids[csgidx++];
|
||||||
|
|
||||||
if (!ret && get_operation(csgpart) == CSGType::Union) {
|
auto op = get_operation(csgpart);
|
||||||
ret = std::move(partgrid);
|
|
||||||
} else if (ret) {
|
if (get_stack_operation(csgpart) == CSGStackOp::Push) {
|
||||||
|
opstack.push({op, nullptr});
|
||||||
|
op = CSGType::Union;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame *top = &opstack.top();
|
||||||
|
|
||||||
|
if (!top->grid && op == CSGType::Union) {
|
||||||
|
top->grid = std::move(partgrid);
|
||||||
|
} else if (top->grid && partgrid) {
|
||||||
switch (get_operation(csgpart)) {
|
switch (get_operation(csgpart)) {
|
||||||
case CSGType::Union:
|
case CSGType::Union:
|
||||||
grid_union(*ret, *partgrid);
|
grid_union(*(top->grid), *partgrid);
|
||||||
break;
|
break;
|
||||||
case CSGType::Difference:
|
case CSGType::Difference:
|
||||||
grid_difference(*ret, *partgrid);
|
grid_difference(*(top->grid), *partgrid);
|
||||||
break;
|
break;
|
||||||
case CSGType::Intersection:
|
case CSGType::Intersection:
|
||||||
grid_intersection(*ret, *partgrid);
|
grid_intersection(*(top->grid), *partgrid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_stack_operation(csgpart) == CSGStackOp::Pop) {
|
||||||
|
VoxelGridPtr popgrid = std::move(top->grid);
|
||||||
|
auto popop = opstack.top().op;
|
||||||
|
opstack.pop();
|
||||||
|
VoxelGridPtr &grid = opstack.top().grid;
|
||||||
|
|
||||||
|
switch (popop) {
|
||||||
|
case CSGType::Union:
|
||||||
|
grid_union(*grid, *popgrid);
|
||||||
|
break;
|
||||||
|
case CSGType::Difference:
|
||||||
|
grid_difference(*grid, *popgrid);
|
||||||
|
break;
|
||||||
|
case CSGType::Intersection:
|
||||||
|
grid_intersection(*grid, *popgrid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = std::move(opstack.top().grid);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1114,7 +1114,7 @@ VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p)
|
||||||
{
|
{
|
||||||
VoxelGridPtr &ret = part.gridcache[p];
|
VoxelGridPtr &ret = part.gridcache[p];
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret && csg::get_mesh(part)) {
|
||||||
p.trafo(csg::get_transform(part));
|
p.trafo(csg::get_transform(part));
|
||||||
ret = mesh_to_grid(*csg::get_mesh(part), p);
|
ret = mesh_to_grid(*csg::get_mesh(part), p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,7 +242,7 @@ void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po)
|
||||||
|
|
||||||
csg::model_to_csgmesh(*po.model_object(), po.trafo(),
|
csg::model_to_csgmesh(*po.model_object(), po.trafo(),
|
||||||
csg_inserter{po.m_mesh_to_slice, slaposAssembly},
|
csg_inserter{po.m_mesh_to_slice, slaposAssembly},
|
||||||
csg::mpartsPositive | csg::mpartsNegative);
|
csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits);
|
||||||
|
|
||||||
generate_preview(po, slaposAssembly);
|
generate_preview(po, slaposAssembly);
|
||||||
}
|
}
|
||||||
|
@ -352,9 +352,18 @@ template<class Cont> BoundingBoxf3 csgmesh_positive_bb(const Cont &csg)
|
||||||
bounding_box(*csg::get_mesh(*csg.begin()),
|
bounding_box(*csg::get_mesh(*csg.begin()),
|
||||||
csg::get_transform(*csg.begin()));
|
csg::get_transform(*csg.begin()));
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
for (const auto &m : csg) {
|
for (const auto &m : csg) {
|
||||||
if (m.operation == csg::CSGType::Union)
|
auto op = csg::get_operation(m);
|
||||||
|
auto stackop = csg::get_stack_operation(m);
|
||||||
|
if (stackop == csg::CSGStackOp::Push && op != csg::CSGType::Union)
|
||||||
|
skip = true;
|
||||||
|
|
||||||
|
if (!skip && csg::get_mesh(m) && op == csg::CSGType::Union)
|
||||||
bb3d.merge(bounding_box(*csg::get_mesh(m), csg::get_transform(m)));
|
bb3d.merge(bounding_box(*csg::get_mesh(m), csg::get_transform(m)));
|
||||||
|
|
||||||
|
if (stackop == csg::CSGStackOp::Pop)
|
||||||
|
skip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bb3d;
|
return bb3d;
|
||||||
|
|
Loading…
Reference in a new issue