Skip to content

Commit

Permalink
Add support for csg operation stacking
Browse files Browse the repository at this point in the history
  • Loading branch information
tamasmeszaros committed Dec 20, 2022
1 parent 4fabd0b commit 25ca46e
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 50 deletions.
12 changes: 12 additions & 0 deletions src/libslic3r/CSGMesh/CSGMesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ namespace Slic3r { namespace csg {

// Supported CSG operation types
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
template<class CSGPartT> CSGType get_operation(const CSGPartT &part)
{
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
template<class CSGPartT>
const indexed_triangle_set *get_mesh(const CSGPartT &part)
Expand All @@ -48,6 +54,11 @@ inline CSGType get_operation(const indexed_triangle_set &part)
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)
{
return &part;
Expand All @@ -63,6 +74,7 @@ struct CSGPart {
AnyPtr<const indexed_triangle_set> its_ptr;
Transform3f trafo;
CSGType operation;
CSGStackOp stack_operation;

CSGPart(AnyPtr<const indexed_triangle_set> ptr = {},
CSGType op = CSGType::Union,
Expand Down
41 changes: 33 additions & 8 deletions src/libslic3r/CSGMesh/ModelToCSGMesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@

#include "libslic3r/Model.hpp"
#include "libslic3r/SLA/Hollowing.hpp"
#include "libslic3r/MeshSplitImpl.hpp"

namespace Slic3r { namespace csg {

enum ModelParts {
mpartsPositive = 1,
mpartsNegative = 2,
mpartsDrillHoles = 4
mpartsDrillHoles = 4,
mpartsDoSplits = 8
};

template<class OutIt>
Expand All @@ -22,20 +24,43 @@ void model_to_csgmesh(const ModelObject &mo,
int parts_to_include = mpartsPositive
)
{
bool do_positives = parts_to_include & mpartsPositive;
bool do_negatives = parts_to_include & mpartsNegative;
bool do_positives = parts_to_include & mpartsPositive;
bool do_negatives = parts_to_include & mpartsNegative;
bool do_drillholes = parts_to_include & mpartsDrillHoles;
bool do_splits = parts_to_include & mpartsDoSplits;

for (const ModelVolume *vol : mo.volumes) {
if (vol && vol->mesh_ptr() &&
((do_positives && vol->is_model_part()) ||
(do_negatives && vol->is_negative_volume()))) {
CSGPart part{&(vol->mesh().its),
vol->is_model_part() ? CSGType::Union : CSGType::Difference,
(trafo * vol->get_matrix()).cast<float>()};

*out = std::move(part);
++out;
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),
vol->is_model_part() ? CSGType::Union : CSGType::Difference,
(trafo * vol->get_matrix()).cast<float>()};

*out = std::move(part);
++out;
}
}
}

Expand Down
119 changes: 86 additions & 33 deletions src/libslic3r/CSGMesh/SliceCSGMesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#define SLICECSGMESH_HPP

#include "CSGMesh.hpp"

#include <stack>

#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Execution/ExecutionTBB.hpp"
Expand All @@ -10,57 +13,107 @@ namespace Slic3r { namespace csg {

template<class ItCSG>
std::vector<ExPolygons> slice_csgmesh_ex(
const Range<ItCSG> &csg,
const Range<ItCSG> &csgrange,
const std::vector<float> &slicegrid,
const MeshSlicingParamsEx &params,
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;
auto trafo = params.trafo;
auto nonempty_indices = reserve_vector<size_t>(slicegrid.size());

for (const auto &m : csg) {
const indexed_triangle_set *its = csg::get_mesh(m);
if (!its)
continue;
if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
opstack.push({CSGType::Union, std::vector<ExPolygons>(slicegrid.size())});

params_cpy.trafo = trafo * csg::get_transform(m).template cast<double>();
std::vector<ExPolygons> slices = slice_mesh_ex(*its,
slicegrid, params_cpy,
throw_on_cancel);
for (const auto &csgpart : csgrange) {
const indexed_triangle_set *its = csg::get_mesh(csgpart);

assert(slices.size() == slicegrid.size());
auto op = get_operation(csgpart);

nonempty_indices.clear();
for (size_t i = 0; i < slicegrid.size(); ++i) {
if (get_operation(m) == CSGType::Intersection || !slices[i].empty())
nonempty_indices.emplace_back(i);
if (get_stack_operation(csgpart) == CSGStackOp::Push) {
opstack.push({op, std::vector<ExPolygons>(slicegrid.size())});
op = CSGType::Union;
}

auto mergefn = [&m, &slices, &ret](size_t i){
switch(get_operation(m)) {
case CSGType::Union:
for (ExPolygon &expoly : slices[i])
ret[i].emplace_back(std::move(expoly));

break;
case CSGType::Difference:
ret[i] = diff_ex(ret[i], slices[i]);
break;
case CSGType::Intersection:
ret[i] = intersection_ex(ret[i], slices[i]);
break;
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,
slicegrid, params_cpy,
throw_on_cancel);

assert(slices.size() == slicegrid.size());

nonempty_indices.clear();
for (size_t i = 0; i < slicegrid.size(); ++i) {
if (op == CSGType::Intersection || !slices[i].empty())
nonempty_indices.emplace_back(i);
}
};

execution::for_each(ex_tbb,
nonempty_indices.begin(), nonempty_indices.end(),
mergefn,
execution::max_concurrency(ex_tbb));
auto mergefn = [&csgpart, &slices, &top](size_t i){
switch(get_operation(csgpart)) {
case CSGType::Union:
for (ExPolygon &expoly : slices[i])
top->slices[i].emplace_back(std::move(expoly));

break;
case CSGType::Difference:
top->slices[i] = diff_ex(top->slices[i], slices[i]);
break;
case CSGType::Intersection:
top->slices[i] = intersection_ex(top->slices[i], slices[i]);
break;
}
};

execution::for_each(ex_tbb,
nonempty_indices.begin(), nonempty_indices.end(),
mergefn,
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) {
auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){
return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON);
Expand Down
49 changes: 43 additions & 6 deletions src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define VOXELIZECSGMESH_HPP

#include <functional>
#include <stack>

#include "CSGMesh.hpp"
#include "libslic3r/OpenVDBUtils.hpp"
Expand Down Expand Up @@ -46,29 +47,65 @@ VoxelGridPtr voxelize_csgmesh(const Range<It> &csgrange,
}, execution::max_concurrency(ex_tbb));

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) {
if (params.statusfn() && params.statusfn()(-1))
break;

auto &partgrid = grids[csgidx++];

if (!ret && get_operation(csgpart) == CSGType::Union) {
ret = std::move(partgrid);
} else if (ret) {
auto op = get_operation(csgpart);

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)) {
case CSGType::Union:
grid_union(*ret, *partgrid);
grid_union(*(top->grid), *partgrid);
break;
case CSGType::Difference:
grid_difference(*ret, *partgrid);
grid_difference(*(top->grid), *partgrid);
break;
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;
}
}
}

ret = std::move(opstack.top().grid);

return ret;
}

Expand Down
2 changes: 1 addition & 1 deletion src/libslic3r/SLAPrint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p)
{
VoxelGridPtr &ret = part.gridcache[p];

if (!ret) {
if (!ret && csg::get_mesh(part)) {
p.trafo(csg::get_transform(part));
ret = mesh_to_grid(*csg::get_mesh(part), p);
}
Expand Down
13 changes: 11 additions & 2 deletions src/libslic3r/SLAPrintSteps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po)

csg::model_to_csgmesh(*po.model_object(), po.trafo(),
csg_inserter{po.m_mesh_to_slice, slaposAssembly},
csg::mpartsPositive | csg::mpartsNegative);
csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits);

generate_preview(po, slaposAssembly);
}
Expand Down Expand Up @@ -352,9 +352,18 @@ template<class Cont> BoundingBoxf3 csgmesh_positive_bb(const Cont &csg)
bounding_box(*csg::get_mesh(*csg.begin()),
csg::get_transform(*csg.begin()));

bool skip = false;
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)));

if (stackop == csg::CSGStackOp::Pop)
skip = false;
}

return bb3d;
Expand Down

0 comments on commit 25ca46e

Please sign in to comment.