Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_sequential_limits

This commit is contained in:
enricoturri1966 2021-05-19 08:27:27 +02:00
commit d60893b990
22 changed files with 561 additions and 488 deletions

View File

@ -1213,32 +1213,32 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
}
else if (! volume->mesh().empty()) {
TriangleMesh upper_mesh, lower_mesh;
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh();
mesh.require_shared_vertices();
// Perform cut
TriangleMeshSlicer tms(&mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh);
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
if (keep_upper) {
upper_mesh.repair();
upper_mesh.reset_repair_stats();
}
if (keep_lower) {
lower_mesh.repair();
lower_mesh.reset_repair_stats();
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
mesh.require_shared_vertices();
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
if (keep_upper) {
upper_mesh = TriangleMesh(upper_its);
upper_mesh.repair();
upper_mesh.reset_repair_stats();
}
if (keep_lower) {
lower_mesh = TriangleMesh(lower_its);
lower_mesh.repair();
lower_mesh.reset_repair_stats();
}
}
if (keep_upper && upper_mesh.facets_count() > 0) {

View File

@ -8,6 +8,7 @@
#include "Flow.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "TriangleMeshSlicer.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
#include "GCode/ThumbnailData.hpp"
@ -24,7 +25,6 @@ class Print;
class PrintObject;
class ModelObject;
class GCode;
enum class SlicingMode : uint32_t;
class Layer;
class SupportLayer;
@ -345,18 +345,18 @@ private:
// so that next call to make_perimeters() performs a union() before computing loops
bool m_typed_slices = false;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below) const;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, MeshSlicingParams::SlicingMode mode) const
{ return this->slice_region(region_id, z, mode, 0, mode); }
std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
std::vector<ExPolygons> slice_volumes(
const std::vector<float> &z,
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below,
const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const
{ return this->slice_volumes(z, mode, 0, mode, volumes); }
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const;
};
struct WipeTowerData

View File

@ -1799,7 +1799,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
bool clipped = false;
bool upscaled = false;
bool spiral_vase = this->print()->config().spiral_vase;
auto slicing_mode = spiral_vase ? SlicingMode::PositiveLargestContour : SlicingMode::Regular;
auto slicing_mode = spiral_vase ? MeshSlicingParams::SlicingMode::PositiveLargestContour : MeshSlicingParams::SlicingMode::Regular;
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
// Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
@ -1815,7 +1815,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON;
++ slicing_mode_normal_below_layer);
}
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, SlicingMode::Regular);
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode::Regular);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@ -2060,7 +2060,7 @@ end:
}
// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below) const
{
std::vector<const ModelVolume*> volumes;
if (region_id < m_region_volumes.size()) {
@ -2120,7 +2120,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
if (volume->is_modifier())
volumes.emplace_back(volume);
}
out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes);
out = this->slice_volumes(slice_zs, MeshSlicingParams::SlicingMode::Regular, volumes);
} else {
// Some modifier in this region was split to layer spans.
std::vector<char> merge;
@ -2138,7 +2138,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume);
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, MeshSlicingParams::SlicingMode::Regular, *model_volume);
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
if (out.empty()) {
out = std::move(this_slices);
@ -2179,7 +2179,7 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z);
return this->slice_volumes(zs, SlicingMode::Regular, volumes);
return this->slice_volumes(zs, MeshSlicingParams::SlicingMode::Regular, volumes);
}
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
@ -2194,7 +2194,7 @@ static void fix_mesh_connectivity(TriangleMesh &mesh)
std::vector<ExPolygons> PrintObject::slice_volumes(
const std::vector<float> &z,
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below,
const std::vector<const ModelVolume*> &volumes) const
{
std::vector<ExPolygons> layers;
@ -2219,18 +2219,17 @@ std::vector<ExPolygons> PrintObject::slice_volumes(
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
// TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
mesh.require_shared_vertices();
MeshSlicingParamsExtended params { { mode, slicing_mode_normal_below_layer, mode_below }, float(m_config.slice_closing_radius.value) };
slice_mesh(mesh, z, params, layers, callback);
MeshSlicingParamsEx params { { mode, slicing_mode_normal_below_layer, mode_below }, float(m_config.slice_closing_radius.value) };
layers = slice_mesh_ex(mesh.its, z, params, [print]() { print->throw_if_canceled(); });
m_print->throw_if_canceled();
}
}
return layers;
}
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
if (! z.empty()) {
@ -2246,13 +2245,12 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, S
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
// TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
MeshSlicingParamsExtended params;
MeshSlicingParamsEx params;
params.mode = mode;
params.closing_radius = float(m_config.slice_closing_radius.value);
slice_mesh(mesh, z, params, layers, callback);
layers = slice_mesh_ex(mesh.its, z, params, [print](){ print->throw_if_canceled(); });
m_print->throw_if_canceled();
}
}
@ -2260,7 +2258,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, S
}
// Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping.
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const
{
std::vector<ExPolygons> out;
if (! z.empty() && ! ranges.empty()) {

View File

@ -296,9 +296,8 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
if (mesh.empty()) return;
mesh.require_shared_vertices();
std::vector<ExPolygons> hole_slices;
slice_mesh(mesh, slicegrid, closing_radius, hole_slices, thr);
std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
if (obj_slices.size() != hole_slices.size())
BOOST_LOG_TRIVIAL(warning)

View File

@ -478,8 +478,8 @@ void pad_blueprint(const TriangleMesh & mesh,
{
if (mesh.empty()) return;
auto out = reserve_vector<ExPolygons>(heights.size());
slice_mesh(mesh, heights, out, thrfn);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> out = slice_mesh_ex(mesh.its, heights, thrfn);
size_t count = 0;
for(auto& o : out) count += o.size();

View File

@ -45,7 +45,8 @@ std::vector<ExPolygons> SupportTree::slice(
if (!sup_mesh.empty()) {
slices.emplace_back();
slice_mesh(sup_mesh, grid, cr, slices.back(), ctl().cancelfn);
assert(sup_mesh.has_shared_vertices());
slices.back() = slice_mesh_ex(sup_mesh.its, grid, cr, ctl().cancelfn);
}
if (!pad_mesh.empty()) {
@ -58,7 +59,8 @@ std::vector<ExPolygons> SupportTree::slice(
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
slice_mesh(pad_mesh, padgrid, cr, slices.back(), ctl().cancelfn);
assert(pad_mesh.has_shared_vertices());
slices.back() = slice_mesh_ex(pad_mesh.its, padgrid, cr, ctl().cancelfn);
}
size_t len = grid.size();

View File

@ -475,7 +475,8 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
float closing_r = float(po.config().slice_closing_radius.value);
auto thr = [this]() { m_print->throw_if_canceled(); };
auto &slice_grid = po.m_model_height_levels;
slice_mesh(mesh, slice_grid, closing_r, po.m_model_slices, thr);
assert(mesh.has_shared_vertices());
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, closing_r, thr);
sla::Interior *interior = po.m_hollowing_data ?
po.m_hollowing_data->interior.get() :
@ -485,8 +486,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
TriangleMesh interiormesh = sla::get_mesh(*interior);
interiormesh.repaired = false;
interiormesh.repair(true);
std::vector<ExPolygons> interior_slices;
slice_mesh(interiormesh, slice_grid, closing_r, interior_slices, thr);
std::vector<ExPolygons> interior_slices = slice_mesh_ex(interiormesh.its, slice_grid, closing_r, thr);
sla::ccr::for_each(size_t(0), interior_slices.size(),
[&po, &interior_slices] (size_t i) {

View File

@ -615,9 +615,8 @@ std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z)
{
// convert doubles to floats
std::vector<float> z_f(z.begin(), z.end());
std::vector<ExPolygons> layers;
slice_mesh(*this, z_f, 0.0004f, layers);
return layers;
assert(this->has_shared_vertices());
return slice_mesh_ex(this->its, z_f, 0.0004f);
}
void TriangleMesh::require_shared_vertices()
@ -686,11 +685,12 @@ std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangl
return index;
}
// Map from a facet edge to a neighbor face index or -1 if no neighbor exists.
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
// Two neighbor faces share a unique edge identifier even if they are flipped.
template<typename ThrowOnCancelCallback>
static inline std::vector<int> create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
{
std::vector<int> out(its.indices.size() * 3, -1);
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
// Create a mapping from triangle edge into face.
struct EdgeToFace {
@ -754,10 +754,10 @@ static inline std::vector<int> create_face_neighbors_index_impl(const indexed_tr
}
}
// Assign an edge index to the 1st face.
out[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges;
if (found) {
EdgeToFace &edge_j = edges_map[j];
out[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
out[edge_j.face](std::abs(edge_j.face_edge) - 1) = num_edges;
// Mark the edge as connected.
edge_j.face = -1;
}
@ -769,16 +769,79 @@ static inline std::vector<int> create_face_neighbors_index_impl(const indexed_tr
return out;
}
std::vector<int> create_face_neighbors_index(const indexed_triangle_set &its)
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its)
{
return create_face_neighbors_index_impl(its, [](){});
}
std::vector<int> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
{
return create_face_neighbors_index_impl(its, throw_on_cancel_callback);
}
// Merge duplicate vertices, return number of vertices removed.
int its_merge_vertices(indexed_triangle_set &its, bool shrink_to_fit)
{
// 1) Sort indices to vertices lexicographically by coordinates AND vertex index.
auto sorted = reserve_vector<int>(its.vertices.size());
for (int i = 0; i < int(its.vertices.size()); ++ i)
sorted.emplace_back(i);
std::sort(sorted.begin(), sorted.end(), [&its](int il, int ir) {
const Vec3f &l = its.vertices[il];
const Vec3f &r = its.vertices[ir];
// Sort lexicographically by coordinates AND vertex index.
return l.x() < r.x() || (l.x() == r.x() && (l.y() < r.y() || (l.y() == r.y() && (l.z() < r.z() || (l.z() == r.z() && il < ir)))));
});
// 2) Map duplicate vertices to the one with the lowest vertex index.
// The vertex to stay will have a map_vertices[...] == -1 index assigned, the other vertices will point to it.
std::vector<int> map_vertices(its.vertices.size(), -1);
for (int i = 0; i < int(sorted.size());) {
const int u = sorted[i];
const Vec3f &p = its.vertices[u];
int j = i;
for (++ j; j < int(sorted.size()); ++ j) {
const int v = sorted[j];
const Vec3f &q = its.vertices[v];
if (p != q)
break;
assert(v > u);
map_vertices[v] = u;
}
i = j;
}
// 3) Shrink its.vertices, update map_vertices with the new vertex indices.
int k = 0;
for (int i = 0; i < int(its.vertices.size()); ++ i) {
if (map_vertices[i] == -1) {
map_vertices[i] = k;
if (k < i)
its.vertices[k] = its.vertices[i];
++ k;
} else {
assert(map_vertices[i] < i);
map_vertices[i] = map_vertices[map_vertices[i]];
}
}
int num_erased = int(its.vertices.size()) - k;
if (num_erased) {
// Shrink the vertices.
its.vertices.erase(its.vertices.begin() + k, its.vertices.end());
// Remap face indices.
for (stl_triangle_vertex_indices &face : its.indices)
for (int i = 0; i < 3; ++ i)
face(i) = map_vertices[face(i)];
// Optionally shrink to fit (reallocate) vertices.
if (shrink_to_fit)
its.vertices.shrink_to_fit();
}
return num_erased;
}
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit)
{
int last = 0;

View File

@ -93,13 +93,24 @@ private:
// vertex.
std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its);
// Map from a facet edge to a neighbor face index or -1 if no neighbor exists.
std::vector<int> create_face_neighbors_index(const indexed_triangle_set &its);
std::vector<int> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
// Two neighbor faces share a unique edge identifier even if they are flipped.
// Used for chaining slice lines into polygons.
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its);
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
// Merge duplicate vertices, return number of vertices removed.
// This function will happily create non-manifolds if more than two faces share the same vertex position
// or more than two faces share the same edge position!
int its_merge_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
// Remove degenerate faces, return number of faces removed.
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = true);
// Remove vertices, which none of the faces references. Return number of freed vertices.
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
void its_shrink_to_fit(indexed_triangle_set &its);
TriangleMesh make_cube(double x, double y, double z);

View File

@ -85,7 +85,7 @@ public:
// feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type { FacetEdgeType::General };
// Used by TriangleMeshSlicer::slice() to skip duplicate edges.
// Used to skip duplicate edges.
enum {
// Triangle edge added, because it has no neighbor.
EDGE0_NO_NEIGHBOR = 0x001,
@ -112,8 +112,15 @@ enum class FacetSliceType {
// Return true, if the facet has been sliced and line_out has been filled.
static FacetSliceType slice_facet(
float slice_z, const stl_vertex *vertices, const stl_triangle_vertex_indices &indices, const int *edge_neighbor,
const int idx_vertex_lowest, const bool horizontal, IntersectionLine *line_out)
// Z height of the slice in XY plane. Scaled or unscaled (same as vertices[].z()).
float slice_z,
// 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
const stl_vertex *vertices,
const stl_triangle_vertex_indices &indices,
const Vec3i &edge_neighbor,
const int idx_vertex_lowest,
const bool horizontal,
IntersectionLine &line_out)
{
IntersectionPoint points[3];
size_t num_points = 0;
@ -129,7 +136,7 @@ static FacetSliceType slice_facet(
{
int k = (idx_vertex_lowest + j) % 3;
int l = (k + 1) % 3;
edge_id = edge_neighbor[k];
edge_id = edge_neighbor(k);
a_id = indices[k];
a = vertices + k;
b_id = indices[l];
@ -147,7 +154,7 @@ static FacetSliceType slice_facet(
FacetSliceType result = FacetSliceType::Slicing;
if (horizontal) {
// All three vertices are aligned with slice_z.
line_out->edge_type = IntersectionLine::FacetEdgeType::Horizontal;
line_out.edge_type = IntersectionLine::FacetEdgeType::Horizontal;
result = FacetSliceType::Cutting;
double normal = (v1.x() - v0.x()) * (v2.y() - v1.y()) - (v1.y() - v0.y()) * (v2.x() - v1.x());
if (normal < 0) {
@ -165,19 +172,19 @@ static FacetSliceType slice_facet(
// in respect to the cutting plane).
result = third_below ? FacetSliceType::Slicing : FacetSliceType::Cutting;
if (third_below) {
line_out->edge_type = IntersectionLine::FacetEdgeType::Top;
line_out.edge_type = IntersectionLine::FacetEdgeType::Top;
std::swap(a, b);
std::swap(a_id, b_id);
} else
line_out->edge_type = IntersectionLine::FacetEdgeType::Bottom;
line_out.edge_type = IntersectionLine::FacetEdgeType::Bottom;
}
line_out->a.x() = a->x();
line_out->a.y() = a->y();
line_out->b.x() = b->x();
line_out->b.y() = b->y();
line_out->a_id = a_id;
line_out->b_id = b_id;
assert(line_out->a != line_out->b);
line_out.a.x() = a->x();
line_out.a.y() = a->y();
line_out.b.x() = b->x();
line_out.b.y() = b->y();
line_out.a_id = a_id;
line_out.b_id = b_id;
assert(line_out.a != line_out.b);
return result;
}
@ -235,31 +242,31 @@ static FacetSliceType slice_facet(
// Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
assert(num_points < 3);
if (num_points == 2) {
line_out->edge_type = IntersectionLine::FacetEdgeType::General;
line_out->a = (Point)points[1];
line_out->b = (Point)points[0];
line_out->a_id = points[1].point_id;
line_out->b_id = points[0].point_id;
line_out->edge_a_id = points[1].edge_id;
line_out->edge_b_id = points[0].edge_id;
line_out.edge_type = IntersectionLine::FacetEdgeType::General;
line_out.a = static_cast<const Point&>(points[1]);
line_out.b = static_cast<const Point&>(points[0]);
line_out.a_id = points[1].point_id;
line_out.b_id = points[0].point_id;
line_out.edge_a_id = points[1].edge_id;
line_out.edge_b_id = points[0].edge_id;
// Not a zero lenght edge.
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//assert(line_out->a != line_out->b);
//assert(line_out.a != line_out.b);
// The plane cuts at least one edge in a general position.
assert(line_out->a_id == -1 || line_out->b_id == -1);
assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
assert(line_out.a_id == -1 || line_out.b_id == -1);
assert(line_out.edge_a_id != -1 || line_out.edge_b_id != -1);
// General slicing position, use the segment for both slicing and object cutting.
#if 0
if (line_out->a_id != -1 && line_out->b_id != -1) {
if (line_out.a_id != -1 && line_out.b_id != -1) {
// Solving a degenerate case, where both the intersections snapped to an edge.
// Correctly classify the face as below or above based on the position of the 3rd point.
int i = indices[0];
if (i == line_out->a_id || i == line_out->b_id)
if (i == line_out.a_id || i == line_out.b_id)
i = indices[1];
if (i == line_out->a_id || i == line_out->b_id)
if (i == line_out.a_id || i == line_out.b_id)
i = indices[2];
assert(i != line_out->a_id && i != line_out->b_id);
line_out->edge_type = ((m_use_quaternion ?
assert(i != line_out.a_id && i != line_out.b_id);
line_out.edge_type = ((m_use_quaternion ?
(m_quaternion * this->v_scaled_shared[i]).z()
: this->v_scaled_shared[i].z()) < slice_z) ? IntersectionLine::FacetEdgeType::Top : IntersectionLine::FacetEdgeType::Bottom;
}
@ -269,40 +276,64 @@ static FacetSliceType slice_facet(
return FacetSliceType::NoSlice;
}
static void slice_facet_at_zs(
template<typename TransformVertex>
void slice_facet_at_zs(
// Scaled or unscaled vertices. transform_vertex_fn may scale zs.
const std::vector<Vec3f> &mesh_vertices,
const TransformVertex &transform_vertex_fn,
const stl_triangle_vertex_indices &indices,
const std::vector<Vec3f> &v_scaled_shared,
const int *facet_neighbors,
const Eigen::Quaternion<float, Eigen::DontAlign> *quaternion,
std::vector<IntersectionLines> *lines,
boost::mutex *lines_mutex,
const std::vector<float> &scaled_zs)
const Vec3i &facet_neighbors,
// Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well.
const std::vector<float> &zs,
std::vector<IntersectionLines> &lines,
boost::mutex &lines_mutex)
{
stl_vertex vertices[3] { v_scaled_shared[indices(0)], v_scaled_shared[indices(1)], v_scaled_shared[indices(2)] };
if (quaternion)
for (int i = 0; i < 3; ++ i)
vertices[i] = *quaternion * vertices[i];
stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
// find facet extents
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
// find layer extents
auto min_layer = std::lower_bound(scaled_zs.begin(), scaled_zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, scaled_zs.end(), max_z); // first layer whose slice_z is > max_z
auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il;
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
if (slice_facet(*it, vertices, indices, facet_neighbors, idx_vertex_lowest, min_z == max_z, &il) == FacetSliceType::Slicing &&
if (slice_facet(*it, vertices, indices, facet_neighbors, idx_vertex_lowest, min_z == max_z, il) == FacetSliceType::Slicing &&
il.edge_type != IntersectionLine::FacetEdgeType::Horizontal) {
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
boost::lock_guard<boost::mutex> l(*lines_mutex);
(*lines)[it - scaled_zs.begin()].emplace_back(il);
boost::lock_guard<boost::mutex> l(lines_mutex);
lines[it - zs.begin()].emplace_back(il);
}
}
}
template<typename TransformVertex, typename ThrowOnCancel>
inline std::vector<IntersectionLines> slice_make_lines(
const std::vector<stl_vertex> &vertices,
const TransformVertex &transform_vertex_fn,
const std::vector<stl_triangle_vertex_indices> &indices,
const std::vector<Vec3i> &face_neighbors,
const std::vector<float> &zs,
const ThrowOnCancel throw_on_cancel_fn)
{
std::vector<IntersectionLines> lines(zs.size(), IntersectionLines());
boost::mutex lines_mutex;
tbb::parallel_for(
tbb::blocked_range<int>(0, int(indices.size())),
[&vertices, &transform_vertex_fn, &indices, &face_neighbors, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
if ((face_idx & 0x0ffff) == 0)
throw_on_cancel_fn();
slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_neighbors[face_idx], zs, lines, lines_mutex);
}
}
);
return lines;
}
#if 0
//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
@ -384,9 +415,9 @@ struct OpenPolyline {
bool consumed;
};
// called by TriangleMeshSlicer::make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
// called by make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
// Only connects segments crossing triangles of the same orientation.
static void chain_lines_by_triangle_connectivity(std::vector<IntersectionLine> &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
{
// Build a map of lines by edge_a_id and a_id.
std::vector<IntersectionLine*> by_edge_a_id;
@ -501,7 +532,7 @@ std::vector<OpenPolyline*> open_polylines_sorted(std::vector<OpenPolyline> &open
return out;
}
// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices.
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices.
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines, Polygons &loops, bool try_connect_reversed)
{
@ -599,7 +630,7 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
}
}
// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices,
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices,
// possibly closing small gaps.
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed)
@ -707,8 +738,11 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
}
}
static void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
static Polygons make_loops(
// Lines will have their flags modified.
IntersectionLines &lines)
{
Polygons loops;
#if 0
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//#ifdef _DEBUG
@ -736,7 +770,7 @@ static void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
std::vector<OpenPolyline> open_polylines;
chain_lines_by_triangle_connectivity(lines, *loops, open_polylines);
chain_lines_by_triangle_connectivity(lines, loops, open_polylines);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
@ -752,8 +786,8 @@ static void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
// Now process the open polylines.
// Do it in two rounds, first try to connect in the same direction only,
// then try to connect the open polylines in reversed order as well.
chain_open_polylines_exact(open_polylines, *loops, false);
chain_open_polylines_exact(open_polylines, *loops, true);
chain_open_polylines_exact(open_polylines, loops, false);
chain_open_polylines_exact(open_polylines, loops, true);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
@ -781,8 +815,8 @@ static void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
}
#else
const double max_gap = 2.; //mm
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, true);
#endif
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -800,6 +834,60 @@ static void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
return loops;
}
template<typename ThrowOnCancel>
static std::vector<Polygons> make_loops(
// Lines will have their flags modified.
std::vector<IntersectionLines> &lines,
const MeshSlicingParams &params,
ThrowOnCancel throw_on_cancel)
{
std::vector<Polygons> layers;
layers.resize(lines.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, lines.size()),
[&lines, &layers, &params, throw_on_cancel](const tbb::blocked_range<size_t> &range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
if ((line_idx & 0x0ffff) == 0)
throw_on_cancel();
Polygons &polygons = layers[line_idx];
polygons = make_loops(lines[line_idx]);
auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
if (! polygons.empty()) {
if (this_mode == MeshSlicingParams::SlicingMode::Positive) {
// Reorient all loops to be CCW.
for (Polygon& p : polygons)
p.make_counter_clockwise();
}
else if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) {
// Keep just the largest polygon, make it CCW.
double max_area = 0.;
Polygon* max_area_polygon = nullptr;
for (Polygon& p : polygons) {
double a = p.area();
if (std::abs(a) > std::abs(max_area)) {
max_area = a;
max_area_polygon = &p;
}
}
assert(max_area_polygon != nullptr);
if (max_area < 0.)
max_area_polygon->reverse();
Polygon p(std::move(*max_area_polygon));
polygons.clear();
polygons.emplace_back(std::move(p));
}
}
}
}
);
return layers;
}
// Used to cut the mesh into two halves.
@ -808,15 +896,11 @@ static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines)
ExPolygons slices;
Polygons holes;
{
Polygons loops;
make_loops(lines, &loops);
for (Polygon &loop : loops)
if (loop.area() >= 0.)
slices.emplace_back(std::move(loop));
else
holes.emplace_back(std::move(loop));
}
for (Polygon &loop : make_loops(lines))
if (loop.area() >= 0.)
slices.emplace_back(std::move(loop));
else
holes.emplace_back(std::move(loop));
// If there are holes, then there should also be outer contours.
assert(holes.empty() || ! slices.empty());
@ -961,138 +1045,63 @@ static void make_expolygons(const Polygons &loops, const float closing_radius, c
union_ex(loops));
}
/*
static void make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices)
{
Polygons pp;
make_loops(lines, &pp);
Slic3r::make_expolygons(pp, closing_radius, 0.f, slices);
}
*/
void TriangleMeshSlicer::init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel)
{
if (! mesh->has_shared_vertices())
throw Slic3r::InvalidArgument("TriangleMeshSlicer was passed a mesh without shared vertices.");
this->init(&mesh->its, throw_on_cancel);
}
void TriangleMeshSlicer::init(const indexed_triangle_set *its, throw_on_cancel_callback_type throw_on_cancel)
{
m_its = its;
facets_edges = create_face_neighbors_index(*its, throw_on_cancel);
v_scaled_shared.assign(its->vertices.size(), stl_vertex());
for (size_t i = 0; i < v_scaled_shared.size(); ++ i)
this->v_scaled_shared[i] = its->vertices[i] / float(SCALING_FACTOR);
}
void TriangleMeshSlicer::set_up_direction(const Vec3f& up)
{
m_quaternion.setFromTwoVectors(up, Vec3f::UnitZ());
m_use_quaternion = true;
}
void TriangleMeshSlicer::slice(
const std::vector<float> &z,
std::vector<Polygons> slice_mesh(
const indexed_triangle_set &mesh,
// Unscaled Zs
const std::vector<float> &zs,
const MeshSlicingParams &params,
std::vector<Polygons> *layers,
throw_on_cancel_callback_type throw_on_cancel) const
std::function<void()> throw_on_cancel)
{
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
BOOST_LOG_TRIVIAL(debug) << "slice_mesh to polygons";
std::vector<IntersectionLines> lines;
/*
This method gets called with a list of unscaled Z coordinates and outputs
a vector pointer having the same number of items as the original list.
Each item is a vector of polygons created by slicing our mesh at the
given heights.
This method should basically combine the behavior of the existing
Perl methods defined in lib/Slic3r/TriangleMesh.pm:
- analyze(): this creates the 'facets_edges' and the 'edges_facets'
tables (we don't need the 'edges' table)
- slice_facet(): this has to be done for each facet. It generates
intersection lines with each plane identified by the Z list.
The get_layer_range() binary search used to identify the Z range
of the facet is already ported to C++ (see Object.xsp)
- make_loops(): this has to be done for each layer. It creates polygons
from the lines generated by the previous step.
At the end, we free the tables generated by analyze() as we don't
need them anymore.
NOTE: this method accepts a vector of floats because the mesh coordinate
type is float.
*/
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice_facet_at_zs";
std::vector<IntersectionLines> lines(z.size());
{
std::vector<float> scaled_z(z);
for (float &z : scaled_z)
z = scaled<float>(z);
boost::mutex lines_mutex;
tbb::parallel_for(
tbb::blocked_range<int>(0, int(m_its->indices.size())),
[&lines, &lines_mutex, &scaled_z, throw_on_cancel, this](const tbb::blocked_range<int>& range) {
const Eigen::Quaternion<float, Eigen::DontAlign> *rotation = m_use_quaternion ? &m_quaternion : nullptr;
for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) {
if ((facet_idx & 0x0ffff) == 0)
throw_on_cancel();
slice_facet_at_zs(m_its->indices[facet_idx], this->v_scaled_shared, this->facets_edges.data() + facet_idx * 3, rotation, &lines, &lines_mutex, scaled_z);
}
//FIXME facets_edges is likely not needed and quite costly to calculate.
// Instead of edge identifiers, one shall use a sorted pair of edge vertex indices.
// However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have
// to make sure that no code relies on it.
std::vector<Vec3i> facets_edges = create_face_neighbors_index(mesh);
const bool identity = params.trafo.matrix() == Transform3d::Identity().matrix();
static constexpr const double s = 1. / SCALING_FACTOR;
if (zs.size() <= 1) {
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
if (identity)
lines = slice_make_lines(
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); },
mesh.indices, facets_edges, zs, throw_on_cancel);
else {
// Transform the vertices, scale up in XY, not in Y.
auto t = params.trafo;
t.prescale(Vec3d(s, s, 1.));
auto tf = t.cast<float>();
slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, facets_edges, zs, throw_on_cancel);
}
);
} else {
// Copy and scale vertices in XY, don't scale in Z.
// Possibly apply the transformation.
std::vector<stl_vertex> vertices(mesh.vertices);
if (identity) {
for (stl_vertex &v : vertices) {
// Scale just XY, leave Z unscaled.
v.x() *= float(s);
v.y() *= float(s);
}
} else {
// Transform the vertices, scale up in XY, not in Y.
auto t = params.trafo;
t.prescale(Vec3d(s, s, 1.));
auto tf = t.cast<float>();
for (stl_vertex &v : vertices)
v = tf * v;
}
lines = slice_make_lines(vertices, [](const Vec3f &p) { return p; }, mesh.indices, facets_edges, zs, throw_on_cancel);
}
}
throw_on_cancel();
// v_scaled_shared could be freed here
// build loops
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::_make_loops_do";
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&lines, &layers, &params, throw_on_cancel](const tbb::blocked_range<size_t>& range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
if ((line_idx & 0x0ffff) == 0)
throw_on_cancel();
Polygons &polygons = (*layers)[line_idx];
make_loops(lines[line_idx], &polygons);
auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
if (! polygons.empty()) {
if (this_mode == SlicingMode::Positive) {
// Reorient all loops to be CCW.
for (Polygon& p : polygons)
p.make_counter_clockwise();
} else if (this_mode == SlicingMode::PositiveLargestContour) {
// Keep just the largest polygon, make it CCW.
double max_area = 0.;
Polygon* max_area_polygon = nullptr;
for (Polygon& p : polygons) {
double a = p.area();
if (std::abs(a) > std::abs(max_area)) {
max_area = a;
max_area_polygon = &p;
}
}
assert(max_area_polygon != nullptr);
if (max_area < 0.)
max_area_polygon->reverse();
Polygon p(std::move(*max_area_polygon));
polygons.clear();
polygons.emplace_back(std::move(p));
}
}
}
}
);
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice finished";
std::vector<Polygons> layers = make_loops(lines, params, throw_on_cancel);
#ifdef SLIC3R_DEBUG
{
@ -1126,65 +1135,107 @@ void TriangleMeshSlicer::slice(
++ iRun;
}
#endif
return layers;
}
void TriangleMeshSlicer::slice(
// Where to slice.
const std::vector<float> &z,
const MeshSlicingParamsExtended &params,
std::vector<ExPolygons> *layers,
throw_on_cancel_callback_type throw_on_cancel) const
std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
const MeshSlicingParamsEx &params,
std::function<void()> throw_on_cancel)
{
std::vector<Polygons> layers_p;
{
MeshSlicingParams slicing_params(params);
if (params.mode == SlicingMode::PositiveLargestContour)
slicing_params.mode = SlicingMode::Positive;
if (params.mode_below == SlicingMode::PositiveLargestContour)
slicing_params.mode_below = SlicingMode::Positive;
this->slice(z, slicing_params, &layers_p, throw_on_cancel);
if (params.mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
if (params.mode_below == MeshSlicingParams::SlicingMode::PositiveLargestContour)
slicing_params.mode_below = MeshSlicingParams::SlicingMode::Positive;
layers_p = slice_mesh(mesh, zs, slicing_params, throw_on_cancel);
}
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
layers->resize(z.size());
// BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - start";
std::vector<ExPolygons> layers(layers_p.size(), ExPolygons{});
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, &params, layers, throw_on_cancel]
tbb::blocked_range<size_t>(0, layers_p.size()),
[&layers_p, &params, &layers, throw_on_cancel]
(const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
throw_on_cancel();
ExPolygons &expolygons = (*layers)[layer_id];
ExPolygons &expolygons = layers[layer_id];
Slic3r::make_expolygons(layers_p[layer_id], params.closing_radius, params.extra_offset, &expolygons);
//FIXME simplify
const auto this_mode = layer_id < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
if (this_mode == SlicingMode::PositiveLargestContour)
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
keep_largest_contour_only(expolygons);
if (params.resolution != 0.)
for (ExPolygon &ex : expolygons)
ex.simplify(params.resolution);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
// BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end";
return layers;
}
static void triangulate_slice(indexed_triangle_set &its, IntersectionLines &lines, const std::vector<int> &slice_vertices, float z)
static void triangulate_slice(
indexed_triangle_set &its,
IntersectionLines &lines,
std::vector<int> &slice_vertices,
// Vertices of the original (unsliced) mesh. Newly added vertices are those on the slice.
int num_original_vertices,
// Z height of the slice.
float z)
{
// BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating the cut";
sort_remove_duplicates(slice_vertices);
ExPolygons section = make_expolygons_simple(lines);
Pointf3s triangles = triangulate_expolygons_3d(section, z, true);
// 1) Create map of the slice vertices from positions to mesh indices.
// As the caller will likely add duplicate points when intersecting triangle edges, there will be duplicates.
std::vector<std::pair<Vec2f, int>> map_vertex_to_index;
map_vertex_to_index.reserve(slice_vertices.size());
for (int i : slice_vertices)
map_vertex_to_index.emplace_back(to_2d(its.vertices[i]), i);
std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[](const std::pair<Vec2f, int> &l, const std::pair<Vec2f, int> &r) {
return l.first.x() < r.first.x() || (l.first.x() == r.first.x() && l.first.y() < r.first.y()); });
return l.first.x() < r.first.x() ||
(l.first.x() == r.first.x() && (l.first.y() < r.first.y() ||
(l.first.y() == r.first.y() && l.second < r.second))); });
// 2) Discover duplicate points on the slice. Remap duplicate vertices to a vertex with a lowest index.
{
std::vector<int> map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1);
int i = 0;
int k = 0;
for (; i < int(map_vertex_to_index.size()); ++ i) {
map_vertex_to_index[k ++] = map_vertex_to_index[i];
const Vec2f &ipos = map_vertex_to_index[i].first;
const int iidx = map_vertex_to_index[i].second;
if (iidx >= num_original_vertices)
// map to itself
map_duplicate_vertex[iidx - num_original_vertices] = iidx;
int j = i;
for (++ j; j < int(map_vertex_to_index.size()) && ipos.x() == map_vertex_to_index[j].first.x() && ipos.y() == map_vertex_to_index[j].first.y(); ++ j) {
const int jidx = map_vertex_to_index[j].second;
assert(jidx >= num_original_vertices);
if (jidx >= num_original_vertices)
// map to the first vertex
map_duplicate_vertex[jidx - num_original_vertices] = iidx;
}
}
map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end());
for (stl_triangle_vertex_indices &f : its.indices)
for (i = 0; i < 3; ++ i)
if (f(i) >= num_original_vertices)
f(i) = map_duplicate_vertex[f(i) - num_original_vertices];
}
size_t idx_vertex_new_first = its.vertices.size();
Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, true);
for (size_t i = 0; i < triangles.size(); ) {
stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) {
Vec3f v = triangles[i++].cast<float>();
Vec3f v = triangles[i ++].cast<float>();
auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[&v](const std::pair<Vec2f, int> &l) { return l.first.x() < v.x() || (l.first.x() == v.x() && l.first.y() < v.y()); });
int idx = -1;
@ -1204,48 +1255,64 @@ static void triangulate_slice(indexed_triangle_set &its, IntersectionLines &line
}
facet(j) = idx;
}
its.indices.emplace_back(facet);
if (facet(0) != facet(1) && facet(0) != facet(2) && facet(1) != facet(2))
its.indices.emplace_back(facet);
}
// Remove vertices, which are not referenced by any face.
its_compactify_vertices(its);
its_remove_degenerate_faces(its);
// Degenerate faces should not be created.
// its_remove_degenerate_faces(its);
}
void TriangleMeshSlicer::cut(float z, indexed_triangle_set *upper, indexed_triangle_set *lower) const
void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *upper, indexed_triangle_set *lower)
{
assert(upper || lower);
if (upper == nullptr && lower == nullptr)
return;
BOOST_LOG_TRIVIAL(trace) << "cut_mesh - slicing object";
if (upper) {
upper->clear();
upper->vertices = m_its->vertices;
upper->indices.reserve(m_its->indices.size());
upper->vertices = mesh.vertices;
upper->indices.reserve(mesh.indices.size());
}
if (lower) {
lower->clear();
lower->vertices = m_its->vertices;
lower->indices.reserve(m_its->indices.size());
lower->vertices = mesh.vertices;
lower->indices.reserve(mesh.indices.size());
}
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object";
const auto scaled_z = scaled<float>(z);
// To triangulate the caps after slicing.
IntersectionLines upper_lines, lower_lines;
std::vector<int> upper_slice_vertices, lower_slice_vertices;
IntersectionLines upper_lines, lower_lines;
std::vector<int> upper_slice_vertices, lower_slice_vertices;
std::vector<Vec3i> facets_edges = create_face_neighbors_index(mesh);
for (int facet_idx = 0; facet_idx < int(m_its->indices.size()); ++ facet_idx) {
const stl_triangle_vertex_indices &facet = m_its->indices[facet_idx];
Vec3f vertices[3] { m_its->vertices[facet(0)], m_its->vertices[facet(1)], m_its->vertices[facet(2)] };
stl_vertex vertices_scaled[3]{ this->v_scaled_shared[facet[0]], this->v_scaled_shared[facet[1]], this->v_scaled_shared[facet[2]] };
for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) {
const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx];
Vec3f vertices[3] { mesh.vertices[facet(0)], mesh.vertices[facet(1)], mesh.vertices[facet(2)] };
float min_z = std::min(vertices[0].z(), std::min(vertices[1].z(), vertices[2].z()));
float max_z = std::max(vertices[0].z(), std::max(vertices[1].z(), vertices[2].z()));
// intersect facet with cutting plane
IntersectionLine line;
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
FacetSliceType slice_type = slice_facet(scaled_z, vertices_scaled, m_its->indices[facet_idx], this->facets_edges.data() + facet_idx * 3, idx_vertex_lowest, min_z == max_z, &line);
FacetSliceType slice_type = FacetSliceType::NoSlice;
if (z > min_z - EPSILON && z < max_z + EPSILON) {
Vec3f vertices_scaled[3];
for (int i = 0; i < 3; ++ i) {
const Vec3f &src = vertices[i];
Vec3f &dst = vertices_scaled[i];
dst.x() = scale_(src.x());
dst.y() = scale_(src.y());
dst.z() = src.z();
}
slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edges[facet_idx], idx_vertex_lowest, min_z == max_z, line);
}
if (slice_type != FacetSliceType::NoSlice) {
// Save intersection lines for generating correct triangulations.
if (line.edge_type == IntersectionLine::FacetEdgeType::Top) {
@ -1274,6 +1341,8 @@ void TriangleMeshSlicer::cut(float z, indexed_triangle_set *upper, indexed_trian
// Facet is cut by the slicing plane.
assert(slice_type == FacetSliceType::Slicing);
assert(line.edge_type == IntersectionLine::FacetEdgeType::General);
assert(line.edge_a_id != -1);
assert(line.edge_b_id != -1);
// look for the vertex on whose side of the slicing plane there are no other vertices
int isolated_vertex =
@ -1282,6 +1351,15 @@ void TriangleMeshSlicer::cut(float z, indexed_triangle_set *upper, indexed_trian
// get vertices starting from the isolated one
int iv = isolated_vertex;
stl_vertex v0v1, v2v0;
assert(facets_edges[facet_idx](iv) == line.edge_a_id ||facets_edges[facet_idx](iv) == line.edge_b_id);
if (facets_edges[facet_idx](iv) == line.edge_a_id) {
v0v1 = to_3d(unscaled<float>(line.a), z);
v2v0 = to_3d(unscaled<float>(line.b), z);
} else {
v0v1 = to_3d(unscaled<float>(line.b), z);
v2v0 = to_3d(unscaled<float>(line.a), z);
}
const stl_vertex &v0 = vertices[iv];
const int iv0 = facet[iv];
if (++ iv == 3)
@ -1294,70 +1372,51 @@ void TriangleMeshSlicer::cut(float z, indexed_triangle_set *upper, indexed_trian
const int iv2 = facet[iv];
// intersect v0-v1 and v2-v0 with cutting plane and make new vertices
auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, float z, float t) {
auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c) {
int iupper, ilower;
if (t <= 0.f)
if (c == a)
iupper = ilower = ia;
else if (t >= 1.f)
else if (c == b)
iupper = ilower = ib;
else {
const stl_vertex c = Vec3f(lerp(a.x(), b.x(), t), lerp(a.y(), b.y(), t), z);
if (c == a)
iupper = ilower = ia;
else if (c == b)
iupper = ilower = ib;
else {
// Insert a new vertex into upper / lower.
if (upper) {
iupper = int(upper->vertices.size());
upper->vertices.emplace_back(c);
upper_slice_vertices.emplace_back(iupper);
}
if (lower) {
ilower = int(lower->vertices.size());
lower->vertices.emplace_back(c);
lower_slice_vertices.emplace_back(ilower);
}
// Insert a new vertex into upper / lower.
if (upper) {
iupper = int(upper->vertices.size());
upper->vertices.emplace_back(c);
upper_slice_vertices.emplace_back(iupper);
}
if (lower) {
ilower = int(lower->vertices.size());
lower->vertices.emplace_back(c);
lower_slice_vertices.emplace_back(ilower);
}
}
return std::make_pair(iupper, ilower);
};
auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, z, (z - v1.z()) / (v0.z() - v1.z()));
auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, z, (z - v2.z()) / (v0.z() - v2.z()));
if (v0(2) > z) {
if (upper != nullptr)
upper->indices.emplace_back(iv0, iv0v1_upper, iv2v0_upper);
if (lower != nullptr) {
lower->indices.emplace_back(iv1, iv2, iv0v1_lower);
lower->indices.emplace_back(iv2, iv2v0_lower, iv0v1_lower);
}
auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v0v1);
auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v2v0);
auto new_face = [](indexed_triangle_set *its, int i, int j, int k) {
if (its != nullptr && i != j && i != k && j != k)
its->indices.emplace_back(i, j, k);
};
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2v0_upper);
new_face(lower, iv1, iv2, iv0v1_lower);
new_face(lower, iv2, iv2v0_lower, iv0v1_lower);
} else {
if (upper != nullptr) {
upper->indices.emplace_back(iv1, iv2, iv0v1_upper);
upper->indices.emplace_back(iv2, iv2v0_upper, iv0v1_upper);
}
if (lower != nullptr)
lower->indices.emplace_back(iv0, iv0v1_lower, iv2v0_lower);
new_face(upper, iv1, iv2, iv0v1_upper);
new_face(upper, iv2, iv2v0_upper, iv0v1_upper);
new_face(lower, iv0, iv0v1_lower, iv2v0_lower);
}
}
}
if (upper != nullptr)
triangulate_slice(*upper, upper_lines, upper_slice_vertices, z);
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z);
if (lower != nullptr)
triangulate_slice(*lower, lower_lines, lower_slice_vertices, z);
}
void TriangleMeshSlicer::cut(float z, TriangleMesh *upper_mesh, TriangleMesh *lower_mesh) const
{
indexed_triangle_set upper, lower;
this->cut(z, upper_mesh ? &upper : nullptr, lower_mesh ? &lower : nullptr);
if (upper_mesh)
*upper_mesh = TriangleMesh(upper);
if (lower_mesh)
*lower_mesh = TriangleMesh(lower);
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z);
}
}

View File

@ -1,140 +1,82 @@
#ifndef slic3r_TriangleMeshSlicer_hpp_
#define slic3r_TriangleMeshSlicer_hpp_
#include "libslic3r.h"
#include <admesh/stl.h>
#include <functional>
#include <vector>
#include <boost/thread.hpp>
#include "BoundingBox.hpp"
#include "Line.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
#include "ExPolygon.hpp"
namespace Slic3r {
class TriangleMesh;
enum class SlicingMode : uint32_t {
// Regular slicing, maintain all contours and their orientation.
Regular,
// Maintain all contours, orient all contours CCW, therefore all holes are being closed.
Positive,
// Orient all contours CCW and keep only the contour with the largest area.
// This mode is useful for slicing complex objects in vase mode.
PositiveLargestContour,
};
struct MeshSlicingParams
{
enum class SlicingMode : uint32_t {
// Regular slicing, maintain all contours and their orientation.
Regular,
// Maintain all contours, orient all contours CCW, therefore all holes are being closed.
Positive,
// Orient all contours CCW and keep only the contour with the largest area.
// This mode is useful for slicing complex objects in vase mode.
PositiveLargestContour,
};
SlicingMode mode { SlicingMode::Regular };
// For vase mode: below this layer a different slicing mode will be used to produce a single contour.
// 0 = ignore.
size_t slicing_mode_normal_below_layer { 0 };
// Mode to apply below slicing_mode_normal_below_layer. Ignored if slicing_mode_nromal_below_layer == 0.
SlicingMode mode_below { SlicingMode::Regular };
// Transforming faces during the slicing.
Transform3d trafo { Transform3d::Identity() };
};
struct MeshSlicingParamsExtended : public MeshSlicingParams
struct MeshSlicingParamsEx : public MeshSlicingParams
{
// Morphological closing operation when creating output expolygons.
float closing_radius { 0 };
// Positive offset applied when creating output expolygons.
float extra_offset { 0 };
// Resolution for contour simplification.
// Resolution for contour simplification, scaled!
// 0 = don't simplify.
double resolution { 0 };
// Transformation of the object owning the ModelVolume.
// Transform3d object_trafo;
};
class TriangleMeshSlicer
std::vector<Polygons> slice_mesh(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
const MeshSlicingParams &params,
std::function<void()> throw_on_cancel = []{});
std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
const MeshSlicingParamsEx &params,
std::function<void()> throw_on_cancel = []{});
inline std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
std::function<void()> throw_on_cancel = []{})
{
public:
using throw_on_cancel_callback_type = std::function<void()>;
TriangleMeshSlicer() = default;
TriangleMeshSlicer(const TriangleMesh *mesh) { this->init(mesh, []{}); }
TriangleMeshSlicer(const indexed_triangle_set *its) { this->init(its, []{}); }
void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void init(const indexed_triangle_set *its, throw_on_cancel_callback_type);
void slice(
const std::vector<float> &z,
const MeshSlicingParams &params,
std::vector<Polygons> *layers,
throw_on_cancel_callback_type throw_on_cancel = []{}) const;
void slice(
// Where to slice.
const std::vector<float> &z,
const MeshSlicingParamsExtended &params,
std::vector<ExPolygons> *layers,
throw_on_cancel_callback_type throw_on_cancel = []{}) const;
void cut(float z, indexed_triangle_set *upper, indexed_triangle_set *lower) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
void set_up_direction(const Vec3f& up);
private:
const indexed_triangle_set *m_its { nullptr };
// const TriangleMesh *mesh { nullptr };
// Map from a facet to an edge index.
std::vector<int> facets_edges;
// Scaled copy of this->mesh->stl.v_shared
std::vector<stl_vertex> v_scaled_shared;
// Quaternion that will be used to rotate every facet before the slicing
Eigen::Quaternion<float, Eigen::DontAlign> m_quaternion;
// Whether or not the above quaterion should be used
bool m_use_quaternion = false;
};
inline void slice_mesh(
const TriangleMesh &mesh,
const std::vector<float> &z,
std::vector<Polygons> &layers,
TriangleMeshSlicer::throw_on_cancel_callback_type thr = []{})
{
if (! mesh.empty()) {
TriangleMeshSlicer slicer(&mesh);
slicer.slice(z, MeshSlicingParams{}, &layers, thr);
}
return slice_mesh_ex(mesh, zs, MeshSlicingParamsEx{}, throw_on_cancel);
}
inline void slice_mesh(
const TriangleMesh &mesh,
const std::vector<float> &z,
const MeshSlicingParamsExtended &params,
std::vector<ExPolygons> &layers,
TriangleMeshSlicer::throw_on_cancel_callback_type thr = []{})
inline std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
float closing_radius,
std::function<void()> throw_on_cancel = []{})
{
if (! mesh.empty()) {
TriangleMeshSlicer slicer(&mesh);
slicer.slice(z, params, &layers, thr);
}
}
inline void slice_mesh(
const TriangleMesh &mesh,
const std::vector<float> &z,
float closing_radius,
std::vector<ExPolygons> &layers,
TriangleMeshSlicer::throw_on_cancel_callback_type thr = []{})
{
MeshSlicingParamsExtended params;
MeshSlicingParamsEx params;
params.closing_radius = closing_radius;
slice_mesh(mesh, z, params, layers);
return slice_mesh_ex(mesh, zs, params, throw_on_cancel);
}
inline void slice_mesh(
const TriangleMesh &mesh,
const std::vector<float> &z,
std::vector<ExPolygons> &layers,
TriangleMeshSlicer::throw_on_cancel_callback_type thr = []{})
{
slice_mesh(mesh, z, MeshSlicingParamsExtended{}, layers);
}
void cut_mesh(
const indexed_triangle_set &mesh,
float z,
indexed_triangle_set *upper,
indexed_triangle_set *lower);
}

View File

@ -2327,6 +2327,7 @@ void ObjectList::update_info_items(size_t obj_idx)
should_show = printer_technology() == ptFFF
&& ! model_object->layer_height_profile.empty();
break;
default: break;
}
if (! shows && should_show) {
@ -2509,8 +2510,8 @@ void ObjectList::select_object_item(bool is_msr_gizmo)
{
if (wxDataViewItem item = GetSelection()) {
ItemType type = m_objects_model->GetItemType(item);
bool is_volume_item = type == itVolume || type == itSettings && m_objects_model->GetItemType(m_objects_model->GetParent(item)) == itVolume;
if (is_msr_gizmo && is_volume_item || type == itObject)
bool is_volume_item = type == itVolume || (type == itSettings && m_objects_model->GetItemType(m_objects_model->GetParent(item)) == itVolume);
if ((is_msr_gizmo && is_volume_item) || type == itObject)
return;
if (wxDataViewItem obj_item = m_objects_model->GetTopParent(item)) {

View File

@ -675,7 +675,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
if (cur_area != bottom_area && fabs(cur_area - bottom_area) > scale_(scale_(1)))
break;
}
if (i < size_t(0.3 * num_layers))
if (i < int(0.3 * num_layers))
continue;
// bottom layer have to be a biggest, so control relation between bottom layer and object size

View File

@ -545,7 +545,7 @@ void MainFrame::init_tabpanel()
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
#if ENABLE_VALIDATE_CUSTOM_GCODE
if (int old_selection = e.GetOldSelection();
old_selection != wxNOT_FOUND && old_selection < m_tabpanel->GetPageCount()) {
old_selection != wxNOT_FOUND && old_selection < static_cast<int>(m_tabpanel->GetPageCount())) {
Tab* old_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(old_selection));
if (old_tab)
old_tab->validate_custom_gcodes();

View File

@ -29,7 +29,6 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh)
m_mesh = &mesh;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_tms.reset(nullptr);
}
}
@ -68,11 +67,6 @@ void MeshClipper::render_cut()
void MeshClipper::recalculate_triangles()
{
if (! m_tms) {
m_tms.reset(new TriangleMeshSlicer);
m_tms->init(m_mesh, [](){});
}
const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
const Vec3f& scaling = m_trafo.get_scaling_factor().cast<float>();
// Calculate clipping plane normal in mesh coordinates.
@ -82,16 +76,15 @@ void MeshClipper::recalculate_triangles()
float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
// Now do the cutting
std::vector<ExPolygons> list_of_expolys;
m_tms->set_up_direction(up.cast<float>());
m_tms->slice(std::vector<float>{height_mesh}, MeshSlicingParamsExtended{}, &list_of_expolys);
MeshSlicingParamsEx slicing_params;
slicing_params.trafo.rotate(Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(up, Vec3d::UnitZ()));
assert(m_mesh->has_shared_vertices());
std::vector<ExPolygons> list_of_expolys = slice_mesh_ex(m_mesh->its, std::vector<float>{height_mesh}, slicing_params);
if (m_negative_mesh && !m_negative_mesh->empty()) {
TriangleMeshSlicer negative_tms{m_negative_mesh};
negative_tms.set_up_direction(up.cast<float>());
std::vector<ExPolygons> neg_polys;
negative_tms.slice(std::vector<float>{height_mesh}, MeshSlicingParamsExtended{}, &neg_polys);
assert(m_negative_mesh->has_shared_vertices());
std::vector<ExPolygons> neg_polys = slice_mesh_ex(m_negative_mesh->its, std::vector<float>{height_mesh}, slicing_params);
list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front());
}

View File

@ -3,7 +3,6 @@
#include "libslic3r/Point.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/SLA/IndexedMesh.hpp"
#include "admesh/stl.h"
@ -96,7 +95,6 @@ private:
std::vector<Vec2f> m_triangles2d;
GLIndexedVertexArray m_vertex_array;
bool m_triangles_valid = false;
std::unique_ptr<TriangleMeshSlicer> m_tms;
};

View File

@ -2044,6 +2044,9 @@ void Plater::priv::update(unsigned int flags)
this->restart_background_process(update_status);
else
this->schedule_background_process();
if (get_config("autocenter") == "1" && this->sidebar->obj_manipul()->IsShown())
this->sidebar->obj_manipul()->UpdateAndShow(true);
}
void Plater::priv::select_view(const std::string& direction)

View File

@ -356,27 +356,25 @@ SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
TriangleMesh cube(vertices, facets);
cube.repair();
WHEN( "Object is cut at the bottom") {
TriangleMesh upper {};
TriangleMesh lower {};
TriangleMeshSlicer slicer(&cube);
slicer.cut(0, &upper, &lower);
indexed_triangle_set upper {};
indexed_triangle_set lower {};
cut_mesh(cube.its, 0, &upper, &lower);
THEN("Upper mesh has all facets except those belonging to the slicing plane.") {
REQUIRE(upper.facets_count() == 12);
REQUIRE(upper.indices.size() == 12);
}
THEN("Lower mesh has no facets.") {
REQUIRE(lower.facets_count() == 0);
REQUIRE(lower.indices.size() == 0);
}
}
WHEN( "Object is cut at the center") {
TriangleMesh upper {};
TriangleMesh lower {};
TriangleMeshSlicer slicer(&cube);
slicer.cut(10, &upper, &lower);
indexed_triangle_set upper {};
indexed_triangle_set lower {};
cut_mesh(cube.its, 10, &upper, &lower);
THEN("Upper mesh has 2 external horizontal facets, 3 facets on each side, and 6 facets on the triangulated side (2 + 12 + 6).") {
REQUIRE(upper.facets_count() == 2+12+6);
REQUIRE(upper.indices.size() == 2+12+6);
}
THEN("Lower mesh has 2 external horizontal facets, 3 facets on each side, and 6 facets on the triangulated side (2 + 12 + 6).") {
REQUIRE(lower.facets_count() == 2+12+6);
REQUIRE(lower.indices.size() == 2+12+6);
}
}
}

View File

@ -320,8 +320,8 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
mesh.translate(tr.x(), tr.y(), tr.z());
bb = mesh.bounding_box();
std::vector<ExPolygons> layers;
slice_mesh(mesh, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh), layers);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> layers = slice_mesh_ex(mesh.its, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh));
sla::RasterBase::Resolution res{2560, 1440};
double disp_w = 120.96;

View File

@ -57,8 +57,8 @@ TEST_CASE("Support point generator should be deterministic if seeded",
auto layer_h = 0.05f;
auto slicegrid = grid(float(gnd), float(zmax), layer_h);
std::vector<ExPolygons> slices;
slice_mesh(mesh, slicegrid, CLOSING_RADIUS, slices);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, slicegrid, CLOSING_RADIUS);
point_gen.seed(0);
point_gen.execute(slices, slicegrid);

View File

@ -2,6 +2,8 @@
#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/SLA/AGGRaster.hpp"
#include <iomanip>
void test_support_model_collision(const std::string &obj_filename,
const sla::SupportTreeConfig &input_supportcfg,
const sla::HollowingConfig &hollowingcfg,
@ -102,7 +104,8 @@ void test_supports(const std::string &obj_filename,
auto layer_h = 0.05f;
out.slicegrid = grid(float(gnd), float(zmax), layer_h);
slice_mesh(mesh, out.slicegrid, CLOSING_RADIUS, out.model_slices);
assert(mesh.has_shared_vertices());
out.model_slices = slice_mesh_ex(mesh.its, out.slicegrid, CLOSING_RADIUS);
sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{});
// Create the special index-triangle mesh with spatial indexing which
@ -466,10 +469,10 @@ sla::SupportPoints calc_support_pts(
const sla::SupportPointGenerator::Config &cfg)
{
// Prepare the slice grid and the slices
std::vector<ExPolygons> slices;
auto bb = cast<float>(mesh.bounding_box());
std::vector<float> heights = grid(bb.min.z(), bb.max.z(), 0.1f);
slice_mesh(mesh, heights, CLOSING_RADIUS, slices);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, heights, CLOSING_RADIUS);
// Prepare the support point calculator
sla::IndexedMesh emesh{mesh};

View File

@ -181,8 +181,7 @@ TriangleMesh::slice(z)
// convert doubles to floats
std::vector<float> z_f = cast<float>(z);
std::vector<ExPolygons> layers;
slice_mesh(*THIS, z_f, 0.049f, layers);
std::vector<ExPolygons> layers = slice_mesh_ex(THIS->its, z_f, 0.049f);
AV* layers_av = newAV();
size_t len = layers.size();
@ -202,14 +201,18 @@ TriangleMesh::slice(z)
RETVAL
void
TriangleMesh::cut(z, upper, lower)
TriangleMesh::cut(z, upper_mesh, lower_mesh)
float z;
TriangleMesh* upper;
TriangleMesh* lower;
TriangleMesh* upper_mesh;
TriangleMesh* lower_mesh;
CODE:
THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer mslicer(THIS);
mslicer.cut(z, upper, lower);
indexed_triangle_set upper, lower;
cut_mesh(THIS->its, z, upper_mesh ? &upper : nullptr, lower_mesh ? &lower : nullptr);
if (upper_mesh)
*upper_mesh = TriangleMesh(upper);
if (lower_mesh)
*lower_mesh = TriangleMesh(lower);
std::vector<double>
TriangleMesh::bb3()