Merge branch 'vb_mmu_top_bottom'
This commit is contained in:
commit
19e3998bd0
@ -602,12 +602,12 @@ FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its)
|
||||
|
||||
std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its)
|
||||
{
|
||||
return create_neighbors_index(ex_seq, its);
|
||||
return create_face_neighbors_index(ex_seq, its);
|
||||
}
|
||||
|
||||
std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its)
|
||||
{
|
||||
return create_neighbors_index(ex_tbb, its);
|
||||
return create_face_neighbors_index(ex_tbb, its);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -64,10 +64,12 @@ public:
|
||||
void set(const std::string §ion, const std::string &key, const std::string &value)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::string key_trimmed = key;
|
||||
boost::trim_all(key_trimmed);
|
||||
assert(key_trimmed == key);
|
||||
assert(! key_trimmed.empty());
|
||||
{
|
||||
std::string key_trimmed = key;
|
||||
boost::trim_all(key_trimmed);
|
||||
assert(key_trimmed == key);
|
||||
assert(! key_trimmed.empty());
|
||||
}
|
||||
#endif // NDEBUG
|
||||
std::string &old = m_storage[section][key];
|
||||
if (old != value) {
|
||||
|
@ -201,21 +201,24 @@ void SeamPlacer::init(const Print& print)
|
||||
|
||||
std::vector<ExPolygons> temp_enf;
|
||||
std::vector<ExPolygons> temp_blk;
|
||||
std::vector<Polygons> temp_polygons;
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
temp_enf.clear();
|
||||
temp_blk.clear();
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, temp_enf);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, temp_blk);
|
||||
|
||||
// Offset the triangles out slightly.
|
||||
for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
|
||||
auto merge_and_offset = [po, &temp_polygons, max_nozzle_dmr](EnforcerBlockerType type, std::vector<ExPolygons>& out) {
|
||||
// Offset the triangles out slightly.
|
||||
temp_polygons.clear();
|
||||
po->project_and_append_custom_facets(true, type, temp_polygons);
|
||||
out.clear();
|
||||
out.reserve(temp_polygons.size());
|
||||
float offset = max_nozzle_dmr + po->config().elefant_foot_compensation;
|
||||
for (ExPolygons& explgs : *custom_per_object) {
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(offset));
|
||||
for (const Polygons &src : temp_polygons) {
|
||||
out.emplace_back(Slic3r::offset_ex(src, scale_(offset)));
|
||||
offset = max_nozzle_dmr;
|
||||
}
|
||||
}
|
||||
};
|
||||
merge_and_offset(EnforcerBlockerType::BLOCKER, temp_blk);
|
||||
merge_and_offset(EnforcerBlockerType::ENFORCER, temp_enf);
|
||||
|
||||
// FIXME: Offsetting should be done somehow cheaper, but following does not work
|
||||
// for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
|
||||
|
@ -201,6 +201,16 @@ protected:
|
||||
virtual ~SupportLayer() = default;
|
||||
};
|
||||
|
||||
template<typename LayerContainer>
|
||||
inline std::vector<float> zs_from_layers(const LayerContainer &layers)
|
||||
{
|
||||
std::vector<float> zs;
|
||||
zs.reserve(layers.size());
|
||||
for (const Layer *l : layers)
|
||||
zs.emplace_back((float)l->slice_z);
|
||||
return zs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
namespace Slic3r {
|
||||
|
||||
template<class ExPolicy>
|
||||
std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its);
|
||||
std::vector<Vec3i> create_face_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its);
|
||||
|
||||
namespace meshsplit_detail {
|
||||
|
||||
@ -24,7 +24,7 @@ template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
|
||||
static const indexed_triangle_set &get_its(const indexed_triangle_set &its) noexcept { return its; }
|
||||
static Index get_index(const indexed_triangle_set &its) noexcept
|
||||
{
|
||||
return create_neighbors_index(ex_tbb, its);
|
||||
return create_face_neighbors_index(ex_tbb, its);
|
||||
}
|
||||
};
|
||||
|
||||
@ -162,28 +162,14 @@ template<class Its> bool its_is_splittable(const Its &m)
|
||||
return !faces.empty();
|
||||
}
|
||||
|
||||
inline int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) {
|
||||
if (int(vertex_index) == triangle_indices[0]) return 0;
|
||||
if (int(vertex_index) == triangle_indices[1]) return 1;
|
||||
if (int(vertex_index) == triangle_indices[2]) return 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
|
||||
{
|
||||
int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1;
|
||||
int vi0 = triangle_indices[edge_index];
|
||||
int vi1 = triangle_indices[next_edge_index];
|
||||
return Vec2crd(vi0, vi1);
|
||||
}
|
||||
|
||||
template<class ExPolicy>
|
||||
std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its)
|
||||
std::vector<Vec3i> create_face_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its)
|
||||
{
|
||||
const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
|
||||
size_t vertices_size = its.vertices.size();
|
||||
|
||||
if (indices.empty() || vertices_size == 0) return {};
|
||||
if (indices.empty()) return {};
|
||||
|
||||
assert(! its.vertices.empty());
|
||||
|
||||
auto vertex_triangles = VertexFaceIndex{its};
|
||||
static constexpr int no_value = -1;
|
||||
@ -192,27 +178,28 @@ std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_
|
||||
|
||||
//for (const stl_triangle_vertex_indices& triangle_indices : indices) {
|
||||
execution::for_each(ex, size_t(0), indices.size(),
|
||||
[&neighbors, &indices, &vertex_triangles] (size_t index)
|
||||
[&neighbors, &indices, &vertex_triangles] (size_t face_idx)
|
||||
{
|
||||
Vec3i& neighbor = neighbors[index];
|
||||
const stl_triangle_vertex_indices & triangle_indices = indices[index];
|
||||
Vec3i& neighbor = neighbors[face_idx];
|
||||
const stl_triangle_vertex_indices & triangle_indices = indices[face_idx];
|
||||
for (int edge_index = 0; edge_index < 3; ++edge_index) {
|
||||
// check if done
|
||||
int& neighbor_edge = neighbor[edge_index];
|
||||
if (neighbor_edge != no_value) continue;
|
||||
Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices);
|
||||
if (neighbor_edge != no_value)
|
||||
// This edge already has a neighbor assigned.
|
||||
continue;
|
||||
Vec2i edge_indices = its_triangle_edge(triangle_indices, edge_index);
|
||||
// IMPROVE: use same vector for 2 sides of triangle
|
||||
const auto &faces_range = vertex_triangles[edge_indices[0]];
|
||||
for (const size_t &face : faces_range) {
|
||||
if (face <= index) continue;
|
||||
const stl_triangle_vertex_indices &face_indices = indices[face];
|
||||
int vertex_index = get_vertex_index(edge_indices[1], face_indices);
|
||||
for (const size_t other_face : vertex_triangles[edge_indices[0]]) {
|
||||
if (other_face <= face_idx) continue;
|
||||
const stl_triangle_vertex_indices &face_indices = indices[other_face];
|
||||
int vertex_index = its_triangle_vertex_index(face_indices, edge_indices[1]);
|
||||
// NOT Contain second vertex?
|
||||
if (vertex_index < 0) continue;
|
||||
// Has NOT oposit direction?
|
||||
// Has NOT oposite direction?
|
||||
if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
|
||||
neighbor_edge = face;
|
||||
neighbors[face][vertex_index] = index;
|
||||
neighbor_edge = other_face;
|
||||
neighbors[other_face][vertex_index] = face_idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1959,8 +1959,19 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data);
|
||||
indexed_triangle_set out = selector.get_facets(type);
|
||||
return out;
|
||||
return selector.get_facets(type);
|
||||
}
|
||||
|
||||
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data);
|
||||
return selector.get_facets_strict(type);
|
||||
}
|
||||
|
||||
bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
return TriangleSelector::has_facets(m_data, type);
|
||||
}
|
||||
|
||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
||||
|
@ -500,10 +500,26 @@ private:
|
||||
};
|
||||
|
||||
enum class EnforcerBlockerType : int8_t {
|
||||
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits!
|
||||
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits.
|
||||
NONE = 0,
|
||||
ENFORCER = 1,
|
||||
BLOCKER = 2
|
||||
BLOCKER = 2,
|
||||
// Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code.
|
||||
Extruder1 = ENFORCER,
|
||||
Extruder2 = BLOCKER,
|
||||
Extruder3,
|
||||
Extruder4,
|
||||
Extruder5,
|
||||
Extruder6,
|
||||
Extruder7,
|
||||
Extruder8,
|
||||
Extruder9,
|
||||
Extruder10,
|
||||
Extruder11,
|
||||
Extruder12,
|
||||
Extruder13,
|
||||
Extruder14,
|
||||
Extruder15,
|
||||
};
|
||||
|
||||
enum class ConversionType : int {
|
||||
@ -521,6 +537,8 @@ public:
|
||||
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { return m_data; }
|
||||
bool set(const TriangleSelector& selector);
|
||||
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
bool empty() const { return m_data.first.empty(); }
|
||||
void clear();
|
||||
|
||||
|
@ -1145,67 +1145,16 @@ static void cut_segmented_layers(const std::vector<ExPolygons>
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - end";
|
||||
}
|
||||
|
||||
// #define MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
|
||||
// Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo
|
||||
static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bottom_layers(const PrintObject &print_object,
|
||||
const std::vector<ExPolygons> &input_expolygons,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1;
|
||||
const size_t num_layers = input_expolygons.size();
|
||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color(num_extruders);
|
||||
triangles_by_color.assign(num_extruders, std::vector<ExPolygons>(layers.size()));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - begin";
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes) {
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) {
|
||||
throw_on_cancel_callback();
|
||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||
if (!mv->is_model_part() || custom_facets.indices.empty())
|
||||
continue;
|
||||
|
||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
||||
for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) {
|
||||
float min_z = std::numeric_limits<float>::max();
|
||||
float max_z = std::numeric_limits<float>::lowest();
|
||||
|
||||
std::array<Vec3f, 3> facet;
|
||||
Points projected_facet(3);
|
||||
for (int p_idx = 0; p_idx < 3; ++p_idx) {
|
||||
facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)];
|
||||
max_z = std::max(max_z, facet[p_idx].z());
|
||||
min_z = std::min(min_z, facet[p_idx].z());
|
||||
}
|
||||
|
||||
// Sort the vertices by z-axis for simplification of projected_facet on slices
|
||||
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||
|
||||
for (int p_idx = 0; p_idx < 3; ++p_idx) {
|
||||
projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y()));
|
||||
projected_facet[p_idx] = projected_facet[p_idx] - print_object.center_offset();
|
||||
}
|
||||
|
||||
ExPolygon triangle = ExPolygon(projected_facet);
|
||||
|
||||
// Find lowest slice not below the triangle.
|
||||
auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
|
||||
auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
|
||||
|
||||
if (last_layer == layers.end())
|
||||
--last_layer;
|
||||
|
||||
if (first_layer == layers.end() || (first_layer != layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON))
|
||||
--first_layer;
|
||||
|
||||
for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it) {
|
||||
size_t layer_idx = layer_it - layers.begin();
|
||||
triangles_by_color[extruder_idx][layer_idx].emplace_back(triangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - end";
|
||||
|
||||
auto get_extrusion_width = [&layers = std::as_const(layers)](const size_t layer_idx) -> float {
|
||||
auto extrusion_width_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(),
|
||||
@ -1235,9 +1184,10 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
return (*top_bottom_layer_it)->region().config().bottom_solid_layers;
|
||||
};
|
||||
|
||||
std::vector<ExPolygons> top_layers(input_expolygons.size());
|
||||
#if 0
|
||||
std::vector<ExPolygons> top_layers(num_layers);
|
||||
top_layers.back() = input_expolygons.back();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, input_expolygons.size()), [&](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_layers), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
|
||||
@ -1245,9 +1195,9 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
||||
std::vector<ExPolygons> bottom_layers(input_expolygons.size());
|
||||
std::vector<ExPolygons> bottom_layers(num_layers);
|
||||
bottom_layers.front() = input_expolygons.front();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input_expolygons.size() - 1), [&](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers - 1), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
|
||||
@ -1255,28 +1205,84 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input_expolygons.size()), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
|
||||
for (std::vector<ExPolygons> &triangles : triangles_by_color) {
|
||||
if (!triangles[layer_idx].empty() && (!top_layers[layer_idx].empty() || !bottom_layers[layer_idx].empty())) {
|
||||
ExPolygons connected = union_ex(offset_ex(triangles[layer_idx], float(10 * SCALED_EPSILON)));
|
||||
triangles[layer_idx] = union_ex(offset_ex(offset_ex(connected, -extrusion_width / 1), extrusion_width / 1));
|
||||
} else {
|
||||
triangles[layer_idx].clear();
|
||||
std::vector<std::vector<ClipperLib::Paths>> triangles_by_color_raw(num_extruders, std::vector<ClipperLib::Paths>(layers.size()));
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - begin";
|
||||
{
|
||||
auto delta = float(10 * SCALED_EPSILON);
|
||||
std::vector<float> deltas { delta, delta, delta };
|
||||
Points projected_facet;
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes)
|
||||
if (mv->is_model_part()) {
|
||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
||||
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) {
|
||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||
if (custom_facets.indices.empty())
|
||||
continue;
|
||||
|
||||
throw_on_cancel_callback();
|
||||
for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) {
|
||||
float min_z = std::numeric_limits<float>::max();
|
||||
float max_z = std::numeric_limits<float>::lowest();
|
||||
|
||||
std::array<Vec3f, 3> facet;
|
||||
for (int p_idx = 0; p_idx < 3; ++p_idx) {
|
||||
facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)];
|
||||
max_z = std::max(max_z, facet[p_idx].z());
|
||||
min_z = std::min(min_z, facet[p_idx].z());
|
||||
}
|
||||
|
||||
// Sort the vertices by z-axis for simplification of projected_facet on slices
|
||||
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||
projected_facet.clear();
|
||||
projected_facet.reserve(3);
|
||||
for (int p_idx = 0; p_idx < 3; ++p_idx)
|
||||
projected_facet.emplace_back(Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())) - print_object.center_offset());
|
||||
if (cross2((projected_facet[1] - projected_facet[0]).cast<int64_t>(), (projected_facet[2] - projected_facet[1]).cast<int64_t>()) < 0)
|
||||
// Make CCW.
|
||||
std::swap(projected_facet[1], projected_facet[2]);
|
||||
ClipperLib::Path offsetted = mittered_offset_path_scaled(projected_facet, deltas, 3.);
|
||||
|
||||
// Find lowest slice not below the triangle.
|
||||
auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
|
||||
auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
|
||||
|
||||
if (last_layer == layers.end())
|
||||
--last_layer;
|
||||
|
||||
if (first_layer == layers.end() || (first_layer != layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON))
|
||||
--first_layer;
|
||||
|
||||
for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it)
|
||||
if (size_t layer_idx = layer_it - layers.begin(); ! top_layers[layer_idx].empty() || ! bottom_layers[layer_idx].empty())
|
||||
triangles_by_color_raw[extruder_idx][layer_idx].emplace_back(offsetted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - end";
|
||||
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color(num_extruders, std::vector<ExPolygons>(layers.size()));
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
float offset_factor = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
|
||||
for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id)
|
||||
if (ClipperLib::Paths &src_paths = triangles_by_color_raw[extruder_id][layer_idx]; !src_paths.empty())
|
||||
triangles_by_color[extruder_id][layer_idx] = offset_ex(offset_ex(ClipperPaths_to_Slic3rExPolygons(src_paths), -offset_factor), offset_factor);
|
||||
}
|
||||
}); // end of parallel_for
|
||||
triangles_by_color_raw.clear();
|
||||
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color_bottom(num_extruders);
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color_top(num_extruders);
|
||||
triangles_by_color_bottom.assign(num_extruders, std::vector<ExPolygons>(input_expolygons.size()));
|
||||
triangles_by_color_top.assign(num_extruders, std::vector<ExPolygons>(input_expolygons.size()));
|
||||
triangles_by_color_bottom.assign(num_extruders, std::vector<ExPolygons>(num_layers));
|
||||
triangles_by_color_top.assign(num_extruders, std::vector<ExPolygons>(num_layers));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - begin";
|
||||
for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) {
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
float extrusion_width = scale_(get_extrusion_width(layer_idx));
|
||||
int top_solid_layers = get_top_solid_layers(layer_idx);
|
||||
ExPolygons top_expolygon = top_layers[layer_idx];
|
||||
@ -1312,7 +1318,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - end";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - begin";
|
||||
for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) {
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
float extrusion_width = scale_(get_extrusion_width(layer_idx));
|
||||
int bottom_solid_layers = get_bottom_solid_layers(layer_idx);
|
||||
const ExPolygons &bottom_expolygon = bottom_layers[layer_idx];
|
||||
@ -1328,7 +1334,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
if (!intersection_poly.empty()) {
|
||||
triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(),
|
||||
intersection_poly.end());
|
||||
for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, input_expolygons.size()); ++last_idx) {
|
||||
for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, num_layers); ++last_idx) {
|
||||
float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width;
|
||||
if (offset_ex(bottom_expolygon, offset_value).empty())
|
||||
continue;
|
||||
@ -1347,8 +1353,8 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - end";
|
||||
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color_merged(num_extruders);
|
||||
triangles_by_color_merged.assign(num_extruders, std::vector<ExPolygons>(input_expolygons.size()));
|
||||
for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) {
|
||||
triangles_by_color_merged.assign(num_extruders, std::vector<ExPolygons>(num_layers));
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) {
|
||||
auto &self = triangles_by_color_merged[color_idx][layer_idx];
|
||||
@ -1363,6 +1369,169 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
triangles_by_color_merged[color_idx - 1][layer_idx]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
// Maximum number of top / bottom layers accounts for maximum overlap of one thread group into a neighbor thread group.
|
||||
int max_top_layers = 0;
|
||||
int max_bottom_layers = 0;
|
||||
int granularity = 1;
|
||||
for (size_t i = 0; i < print_object.num_printing_regions(); ++ i) {
|
||||
const PrintRegionConfig &config = print_object.printing_region(i).config();
|
||||
max_top_layers = std::max(max_top_layers, config.top_solid_layers.value);
|
||||
max_bottom_layers = std::max(max_bottom_layers, config.bottom_solid_layers.value);
|
||||
granularity = std::max(granularity, std::max(config.top_solid_layers.value, config.bottom_solid_layers.value) - 1);
|
||||
}
|
||||
|
||||
// Project upwards pointing painted triangles over top surfaces,
|
||||
// project downards pointing painted triangles over bottom surfaces.
|
||||
std::vector<std::vector<Polygons>> top_raw(num_extruders), bottom_raw(num_extruders);
|
||||
std::vector<float> zs = zs_from_layers(print_object.layers());
|
||||
Transform3d object_trafo = print_object.trafo();
|
||||
object_trafo.pretranslate(Vec3d(- unscale<double>(print_object.center_offset().x()), - unscale<double>(print_object.center_offset().y()), 0));
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
static int iRun = 0;
|
||||
#endif // NDEBUG
|
||||
|
||||
if (max_top_layers > 0 || max_bottom_layers > 0) {
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes)
|
||||
if (mv->is_model_part()) {
|
||||
const Transform3d volume_trafo = object_trafo * mv->get_matrix();
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) {
|
||||
const indexed_triangle_set painted = mv->mmu_segmentation_facets.get_facets_strict(*mv, EnforcerBlockerType(extruder_idx));
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
{
|
||||
static int iRun = 0;
|
||||
its_write_obj(painted, debug_out_path("mm-painted-patch-%d-%d.obj", iRun ++, extruder_idx).c_str());
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
if (! painted.indices.empty()) {
|
||||
std::vector<Polygons> top, bottom;
|
||||
slice_mesh_slabs(painted, zs, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, throw_on_cancel_callback);
|
||||
auto merge = [](std::vector<Polygons> &&src, std::vector<Polygons> &dst) {
|
||||
auto it_src = find_if(src.begin(), src.end(), [](const Polygons &p){ return ! p.empty(); });
|
||||
if (it_src != src.end()) {
|
||||
if (dst.empty()) {
|
||||
dst = std::move(src);
|
||||
} else {
|
||||
assert(src.size() == dst.size());
|
||||
auto it_dst = dst.begin() + (it_src - src.begin());
|
||||
for (; it_src != src.end(); ++ it_src, ++ it_dst)
|
||||
if (! it_src->empty()) {
|
||||
if (it_dst->empty())
|
||||
*it_dst = std::move(*it_src);
|
||||
else
|
||||
append(*it_dst, std::move(*it_src));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
merge(std::move(top), top_raw[extruder_idx]);
|
||||
merge(std::move(bottom), bottom_raw[extruder_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
{
|
||||
const char* colors[] = { "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "yellow" };
|
||||
static int iRun = 0;
|
||||
for (size_t layer_id = 0; layer_id < zs.size(); ++layer_id) {
|
||||
std::vector<std::pair<Slic3r::ExPolygons, SVG::ExPolygonAttributes>> svg;
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) {
|
||||
if (! top_raw[extruder_idx].empty() && ! top_raw[extruder_idx][layer_id].empty())
|
||||
if (ExPolygons expoly = union_ex(top_raw[extruder_idx][layer_id]); ! expoly.empty()) {
|
||||
const char *color = colors[extruder_idx];
|
||||
svg.emplace_back(expoly, SVG::ExPolygonAttributes{ format("top%d", extruder_idx), color, color, color });
|
||||
}
|
||||
if (! bottom_raw[extruder_idx].empty() && ! bottom_raw[extruder_idx][layer_id].empty())
|
||||
if (ExPolygons expoly = union_ex(bottom_raw[extruder_idx][layer_id]); ! expoly.empty()) {
|
||||
const char *color = colors[extruder_idx + 8];
|
||||
svg.emplace_back(expoly, SVG::ExPolygonAttributes{ format("bottom%d", extruder_idx), color, color, color });
|
||||
}
|
||||
}
|
||||
SVG::export_expolygons(debug_out_path("mm-segmentation-top-bottom-%d-%d-%lf.svg", iRun, layer_id, zs[layer_id]), svg);
|
||||
}
|
||||
++ iRun;
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color_bottom(num_extruders);
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color_top(num_extruders);
|
||||
triangles_by_color_bottom.assign(num_extruders, std::vector<ExPolygons>(num_layers * 2));
|
||||
triangles_by_color_top.assign(num_extruders, std::vector<ExPolygons>(num_layers * 2));
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers, granularity), [&](const tbb::blocked_range<size_t> &range) {
|
||||
size_t group_idx = range.begin() / granularity;
|
||||
size_t layer_idx_offset = (group_idx & 1) * num_layers;
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
float extrusion_width = scale_(get_extrusion_width(layer_idx));
|
||||
int top_solid_layers = get_top_solid_layers(layer_idx);
|
||||
int bottom_solid_layers = get_bottom_solid_layers(layer_idx);
|
||||
float narrow_island_width = 0.1f * float(extrusion_width);
|
||||
for (size_t color_idx = 0; color_idx < num_extruders; ++ color_idx) {
|
||||
throw_on_cancel_callback();
|
||||
if (std::vector<Polygons> &top = top_raw[color_idx]; ! top.empty() && ! top[layer_idx].empty())
|
||||
if (ExPolygons top_ex = union_ex(top[layer_idx]); ! top_ex.empty()) {
|
||||
// Clean up thin projections. They are not printable anyways.
|
||||
top_ex = offset2_ex(top_ex, - narrow_island_width, + narrow_island_width);
|
||||
if (! top_ex.empty()) {
|
||||
append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex);
|
||||
float offset = 0.f;
|
||||
ExPolygons layer_slices_trimmed = input_expolygons[layer_idx];
|
||||
for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - top_solid_layers), int(0)); --last_idx) {
|
||||
offset -= extrusion_width;
|
||||
layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]);
|
||||
ExPolygons last = intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset));
|
||||
if (last.empty())
|
||||
break;
|
||||
append(triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (std::vector<Polygons> &bottom = bottom_raw[color_idx]; ! bottom.empty() && ! bottom[layer_idx].empty())
|
||||
if (ExPolygons bottom_ex = union_ex(bottom[layer_idx]); ! bottom_ex.empty()) {
|
||||
// Clean up thin projections. They are not printable anyways.
|
||||
bottom_ex = offset2_ex(bottom_ex, - narrow_island_width, + narrow_island_width);
|
||||
if (! bottom_ex.empty()) {
|
||||
append(triangles_by_color_bottom[color_idx][layer_idx + layer_idx_offset], bottom_ex);
|
||||
float offset = 0.f;
|
||||
ExPolygons layer_slices_trimmed = input_expolygons[layer_idx];
|
||||
for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, num_layers); ++last_idx) {
|
||||
offset -= extrusion_width;
|
||||
layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]);
|
||||
ExPolygons last = intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset));
|
||||
if (last.empty())
|
||||
break;
|
||||
append(triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<std::vector<ExPolygons>> triangles_by_color_merged(num_extruders);
|
||||
triangles_by_color_merged.assign(num_extruders, std::vector<ExPolygons>(num_layers));
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) {
|
||||
auto &self = triangles_by_color_merged[color_idx][layer_idx];
|
||||
append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx]));
|
||||
append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx + num_layers]));
|
||||
append(self, std::move(triangles_by_color_top[color_idx][layer_idx]));
|
||||
append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers]));
|
||||
self = union_ex(self);
|
||||
}
|
||||
// Trim one region by the other if some of the regions overlap.
|
||||
for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++ color_idx)
|
||||
triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx],
|
||||
triangles_by_color_merged[color_idx - 1][layer_idx]);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
return triangles_by_color_merged;
|
||||
}
|
||||
|
@ -655,10 +655,8 @@ std::string Print::validate(std::string* warning) const
|
||||
// Notify the user in that case.
|
||||
if (! object->has_support() && warning) {
|
||||
for (const ModelVolume* mv : object->model_object()->volumes) {
|
||||
bool has_enforcers = mv->is_support_enforcer()
|
||||
|| (mv->is_model_part()
|
||||
&& ! mv->supported_facets.empty()
|
||||
&& ! mv->supported_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER).indices.empty());
|
||||
bool has_enforcers = mv->is_support_enforcer() ||
|
||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
if (has_enforcers) {
|
||||
*warning = "_SUPPORTS_OFF";
|
||||
break;
|
||||
|
@ -326,12 +326,12 @@ public:
|
||||
void slice();
|
||||
|
||||
// Helpers to slice support enforcer / blocker meshes by the support generator.
|
||||
std::vector<ExPolygons> slice_support_volumes(const ModelVolumeType model_volume_type) const;
|
||||
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
|
||||
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||
std::vector<Polygons> slice_support_volumes(const ModelVolumeType model_volume_type) const;
|
||||
std::vector<Polygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
|
||||
std::vector<Polygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||
|
||||
// Helpers to project custom facets on slices
|
||||
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const;
|
||||
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<Polygons>& expolys) const;
|
||||
|
||||
private:
|
||||
// to be called from Print only.
|
||||
|
@ -402,10 +402,8 @@ void PrintObject::generate_support_material()
|
||||
// Notify the user in that case.
|
||||
if (! this->has_support()) {
|
||||
for (const ModelVolume* mv : this->model_object()->volumes) {
|
||||
bool has_enforcers = mv->is_support_enforcer()
|
||||
|| (mv->is_model_part()
|
||||
&& ! mv->supported_facets.empty()
|
||||
&& ! mv->supported_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER).indices.empty());
|
||||
bool has_enforcers = mv->is_support_enforcer() ||
|
||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
if (has_enforcers) {
|
||||
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
|
||||
L("An object has custom support enforcers which will not be used "
|
||||
@ -2101,206 +2099,206 @@ void PrintObject::_generate_support_material()
|
||||
support_material.generate(*this);
|
||||
}
|
||||
|
||||
|
||||
void PrintObject::project_and_append_custom_facets(
|
||||
bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const
|
||||
static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector<Polygons> &out)
|
||||
{
|
||||
for (const ModelVolume* mv : this->model_object()->volumes) {
|
||||
const indexed_triangle_set custom_facets = seam
|
||||
? mv->seam_facets.get_facets(*mv, type)
|
||||
: mv->supported_facets.get_facets(*mv, type);
|
||||
if (! mv->is_model_part() || custom_facets.indices.empty())
|
||||
if (custom_facets.indices.empty())
|
||||
return;
|
||||
|
||||
const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f);
|
||||
|
||||
// The projection will be at most a pentagon. Let's minimize heap
|
||||
// reallocations by saving in in the following struct.
|
||||
// Points are used so that scaling can be done in parallel
|
||||
// and they can be moved from to create an ExPolygon later.
|
||||
struct LightPolygon {
|
||||
LightPolygon() { pts.reserve(5); }
|
||||
LightPolygon(const std::array<Vec2f, 3>& tri) {
|
||||
pts.reserve(3);
|
||||
pts.emplace_back(scaled<coord_t>(tri.front()));
|
||||
pts.emplace_back(scaled<coord_t>(tri[1]));
|
||||
pts.emplace_back(scaled<coord_t>(tri.back()));
|
||||
}
|
||||
|
||||
Points pts;
|
||||
|
||||
void add(const Vec2f& pt) {
|
||||
pts.emplace_back(scaled<coord_t>(pt));
|
||||
assert(pts.size() <= 5);
|
||||
}
|
||||
};
|
||||
|
||||
// Structure to collect projected polygons. One element for each triangle.
|
||||
// Saves vector of polygons and layer_id of the first one.
|
||||
struct TriangleProjections {
|
||||
size_t first_layer_id;
|
||||
std::vector<LightPolygon> polygons;
|
||||
};
|
||||
|
||||
// Vector to collect resulting projections from each triangle.
|
||||
std::vector<TriangleProjections> projections_of_triangles(custom_facets.indices.size());
|
||||
|
||||
// Iterate over all triangles.
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, custom_facets.indices.size()),
|
||||
[&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
|
||||
|
||||
std::array<Vec3f, 3> facet;
|
||||
|
||||
// Transform the triangle into worlds coords.
|
||||
for (int i=0; i<3; ++i)
|
||||
facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)];
|
||||
|
||||
// Ignore triangles with upward-pointing normal. Don't forget about mirroring.
|
||||
float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z();
|
||||
if (! seam && tr_det_sign * z_comp > 0.)
|
||||
continue;
|
||||
|
||||
const Transform3f& tr1 = mv->get_matrix().cast<float>();
|
||||
const Transform3f& tr2 = this->trafo().cast<float>();
|
||||
const Transform3f tr = tr2 * tr1;
|
||||
const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f);
|
||||
const Vec2f center = unscaled<float>(this->center_offset());
|
||||
ConstLayerPtrsAdaptor layers = this->layers();
|
||||
// The algorithm does not process vertical triangles, but it should for seam.
|
||||
// In that case, tilt the triangle a bit so the projection does not degenerate.
|
||||
if (seam && z_comp == 0.f)
|
||||
facet[0].x() += float(EPSILON);
|
||||
|
||||
// The projection will be at most a pentagon. Let's minimize heap
|
||||
// reallocations by saving in in the following struct.
|
||||
// Points are used so that scaling can be done in parallel
|
||||
// and they can be moved from to create an ExPolygon later.
|
||||
struct LightPolygon {
|
||||
LightPolygon() { pts.reserve(5); }
|
||||
LightPolygon(const std::array<Vec2f, 3>& tri) {
|
||||
pts.reserve(3);
|
||||
pts.emplace_back(scaled<coord_t>(tri.front()));
|
||||
pts.emplace_back(scaled<coord_t>(tri[1]));
|
||||
pts.emplace_back(scaled<coord_t>(tri.back()));
|
||||
}
|
||||
// Sort the three vertices according to z-coordinate.
|
||||
std::sort(facet.begin(), facet.end(),
|
||||
[](const Vec3f& pt1, const Vec3f&pt2) {
|
||||
return pt1.z() < pt2.z();
|
||||
});
|
||||
|
||||
Points pts;
|
||||
std::array<Vec2f, 3> trianglef;
|
||||
for (int i=0; i<3; ++i)
|
||||
trianglef[i] = to_2d(facet[i]);
|
||||
|
||||
void add(const Vec2f& pt) {
|
||||
pts.emplace_back(scaled<coord_t>(pt));
|
||||
assert(pts.size() <= 5);
|
||||
}
|
||||
};
|
||||
|
||||
// Structure to collect projected polygons. One element for each triangle.
|
||||
// Saves vector of polygons and layer_id of the first one.
|
||||
struct TriangleProjections {
|
||||
size_t first_layer_id;
|
||||
std::vector<LightPolygon> polygons;
|
||||
};
|
||||
|
||||
// Vector to collect resulting projections from each triangle.
|
||||
std::vector<TriangleProjections> projections_of_triangles(custom_facets.indices.size());
|
||||
|
||||
// Iterate over all triangles.
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, custom_facets.indices.size()),
|
||||
[center, &custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
|
||||
|
||||
std::array<Vec3f, 3> facet;
|
||||
|
||||
// Transform the triangle into worlds coords.
|
||||
for (int i=0; i<3; ++i)
|
||||
facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)];
|
||||
|
||||
// Ignore triangles with upward-pointing normal. Don't forget about mirroring.
|
||||
float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z();
|
||||
if (! seam && tr_det_sign * z_comp > 0.)
|
||||
continue;
|
||||
|
||||
// The algorithm does not process vertical triangles, but it should for seam.
|
||||
// In that case, tilt the triangle a bit so the projection does not degenerate.
|
||||
if (seam && z_comp == 0.f)
|
||||
facet[0].x() += float(EPSILON);
|
||||
|
||||
// Sort the three vertices according to z-coordinate.
|
||||
std::sort(facet.begin(), facet.end(),
|
||||
[](const Vec3f& pt1, const Vec3f&pt2) {
|
||||
return pt1.z() < pt2.z();
|
||||
// Find lowest slice not below the triangle.
|
||||
auto it = std::lower_bound(layers.begin(), layers.end(), facet[0].z()+EPSILON,
|
||||
[](const Layer* l1, float z) {
|
||||
return l1->slice_z < z;
|
||||
});
|
||||
|
||||
std::array<Vec2f, 3> trianglef;
|
||||
for (int i=0; i<3; ++i)
|
||||
trianglef[i] = to_2d(facet[i]) - center;
|
||||
// Count how many projections will be generated for this triangle
|
||||
// and allocate respective amount in projections_of_triangles.
|
||||
size_t first_layer_id = projections_of_triangles[idx].first_layer_id = it - layers.begin();
|
||||
size_t last_layer_id = first_layer_id;
|
||||
// The cast in the condition below is important. The comparison must
|
||||
// be an exact opposite of the one lower in the code where
|
||||
// the polygons are appended. And that one is on floats.
|
||||
while (last_layer_id + 1 < layers.size()
|
||||
&& float(layers[last_layer_id]->slice_z) <= facet[2].z())
|
||||
++last_layer_id;
|
||||
|
||||
// Find lowest slice not below the triangle.
|
||||
auto it = std::lower_bound(layers.begin(), layers.end(), facet[0].z()+EPSILON,
|
||||
[](const Layer* l1, float z) {
|
||||
return l1->slice_z < z;
|
||||
});
|
||||
|
||||
// Count how many projections will be generated for this triangle
|
||||
// and allocate respective amount in projections_of_triangles.
|
||||
size_t first_layer_id = projections_of_triangles[idx].first_layer_id = it - layers.begin();
|
||||
size_t last_layer_id = first_layer_id;
|
||||
// The cast in the condition below is important. The comparison must
|
||||
// be an exact opposite of the one lower in the code where
|
||||
// the polygons are appended. And that one is on floats.
|
||||
while (last_layer_id + 1 < layers.size()
|
||||
&& float(layers[last_layer_id]->slice_z) <= facet[2].z())
|
||||
++last_layer_id;
|
||||
|
||||
if (first_layer_id == last_layer_id) {
|
||||
// The triangle fits just a single slab, just project it. This also avoids division by zero for horizontal triangles.
|
||||
float dz = facet[2].z() - facet[0].z();
|
||||
assert(dz >= 0);
|
||||
// The face is nearly horizontal and it crosses the slicing plane at first_layer_id - 1.
|
||||
// Rather add this face to both the planes.
|
||||
bool add_below = dz < float(2. * EPSILON) && first_layer_id > 0 && layers[first_layer_id - 1]->slice_z > facet[0].z() - EPSILON;
|
||||
projections_of_triangles[idx].polygons.reserve(add_below ? 2 : 1);
|
||||
if (first_layer_id == last_layer_id) {
|
||||
// The triangle fits just a single slab, just project it. This also avoids division by zero for horizontal triangles.
|
||||
float dz = facet[2].z() - facet[0].z();
|
||||
assert(dz >= 0);
|
||||
// The face is nearly horizontal and it crosses the slicing plane at first_layer_id - 1.
|
||||
// Rather add this face to both the planes.
|
||||
bool add_below = dz < float(2. * EPSILON) && first_layer_id > 0 && layers[first_layer_id - 1]->slice_z > facet[0].z() - EPSILON;
|
||||
projections_of_triangles[idx].polygons.reserve(add_below ? 2 : 1);
|
||||
projections_of_triangles[idx].polygons.emplace_back(trianglef);
|
||||
if (add_below) {
|
||||
-- projections_of_triangles[idx].first_layer_id;
|
||||
projections_of_triangles[idx].polygons.emplace_back(trianglef);
|
||||
if (add_below) {
|
||||
-- projections_of_triangles[idx].first_layer_id;
|
||||
projections_of_triangles[idx].polygons.emplace_back(trianglef);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
projections_of_triangles[idx].polygons.resize(last_layer_id - first_layer_id + 1);
|
||||
|
||||
// Calculate how to move points on triangle sides per unit z increment.
|
||||
Vec2f ta(trianglef[1] - trianglef[0]);
|
||||
Vec2f tb(trianglef[2] - trianglef[0]);
|
||||
ta *= 1.f/(facet[1].z() - facet[0].z());
|
||||
tb *= 1.f/(facet[2].z() - facet[0].z());
|
||||
|
||||
// Projection on current slice will be build directly in place.
|
||||
LightPolygon* proj = &projections_of_triangles[idx].polygons[0];
|
||||
proj->add(trianglef[0]);
|
||||
|
||||
bool passed_first = false;
|
||||
bool stop = false;
|
||||
|
||||
// Project a sub-polygon on all slices intersecting the triangle.
|
||||
while (it != layers.end()) {
|
||||
const float z = float((*it)->slice_z);
|
||||
|
||||
// Projections of triangle sides intersections with slices.
|
||||
// a moves along one side, b tracks the other.
|
||||
Vec2f a;
|
||||
Vec2f b;
|
||||
|
||||
// If the middle vertex was already passed, append the vertex
|
||||
// and use ta for tracking the remaining side.
|
||||
if (z > facet[1].z() && ! passed_first) {
|
||||
proj->add(trianglef[1]);
|
||||
ta = trianglef[2]-trianglef[1];
|
||||
ta *= 1.f/(facet[2].z() - facet[1].z());
|
||||
passed_first = true;
|
||||
}
|
||||
|
||||
// This slice is above the triangle already.
|
||||
if (z > facet[2].z() || it+1 == layers.end()) {
|
||||
proj->add(trianglef[2]);
|
||||
stop = true;
|
||||
}
|
||||
else {
|
||||
// Move a, b along the side it currently tracks to get
|
||||
// projected intersection with current slice.
|
||||
a = passed_first ? (trianglef[1]+ta*(z-facet[1].z()))
|
||||
: (trianglef[0]+ta*(z-facet[0].z()));
|
||||
b = trianglef[0]+tb*(z-facet[0].z());
|
||||
proj->add(a);
|
||||
proj->add(b);
|
||||
}
|
||||
|
||||
if (stop)
|
||||
break;
|
||||
|
||||
// Advance to the next layer.
|
||||
++it;
|
||||
++proj;
|
||||
assert(proj <= &projections_of_triangles[idx].polygons.back() );
|
||||
|
||||
// a, b are first two points of the polygon for the next layer.
|
||||
proj->add(b);
|
||||
proj->add(a);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
||||
// Make sure that the output vector can be used.
|
||||
expolys.resize(layers.size());
|
||||
projections_of_triangles[idx].polygons.resize(last_layer_id - first_layer_id + 1);
|
||||
|
||||
// Now append the collected polygons to respective layers.
|
||||
for (auto& trg : projections_of_triangles) {
|
||||
int layer_id = int(trg.first_layer_id);
|
||||
for (LightPolygon &poly : trg.polygons) {
|
||||
if (layer_id >= int(expolys.size()))
|
||||
break; // part of triangle could be projected above top layer
|
||||
assert(! poly.pts.empty());
|
||||
// The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well.
|
||||
// Calculate how to move points on triangle sides per unit z increment.
|
||||
Vec2f ta(trianglef[1] - trianglef[0]);
|
||||
Vec2f tb(trianglef[2] - trianglef[0]);
|
||||
ta *= 1.f/(facet[1].z() - facet[0].z());
|
||||
tb *= 1.f/(facet[2].z() - facet[0].z());
|
||||
|
||||
// Projection on current slice will be built directly in place.
|
||||
LightPolygon* proj = &projections_of_triangles[idx].polygons[0];
|
||||
proj->add(trianglef[0]);
|
||||
|
||||
bool passed_first = false;
|
||||
bool stop = false;
|
||||
|
||||
// Project a sub-polygon on all slices intersecting the triangle.
|
||||
while (it != layers.end()) {
|
||||
const float z = float((*it)->slice_z);
|
||||
|
||||
// Projections of triangle sides intersections with slices.
|
||||
// a moves along one side, b tracks the other.
|
||||
Vec2f a;
|
||||
Vec2f b;
|
||||
|
||||
// If the middle vertex was already passed, append the vertex
|
||||
// and use ta for tracking the remaining side.
|
||||
if (z > facet[1].z() && ! passed_first) {
|
||||
proj->add(trianglef[1]);
|
||||
ta = trianglef[2]-trianglef[1];
|
||||
ta *= 1.f/(facet[2].z() - facet[1].z());
|
||||
passed_first = true;
|
||||
}
|
||||
|
||||
// This slice is above the triangle already.
|
||||
if (z > facet[2].z() || it+1 == layers.end()) {
|
||||
proj->add(trianglef[2]);
|
||||
stop = true;
|
||||
}
|
||||
else {
|
||||
// Move a, b along the side it currently tracks to get
|
||||
// projected intersection with current slice.
|
||||
a = passed_first ? (trianglef[1]+ta*(z-facet[1].z()))
|
||||
: (trianglef[0]+ta*(z-facet[0].z()));
|
||||
b = trianglef[0]+tb*(z-facet[0].z());
|
||||
proj->add(a);
|
||||
proj->add(b);
|
||||
}
|
||||
|
||||
if (stop)
|
||||
break;
|
||||
|
||||
// Advance to the next layer.
|
||||
++it;
|
||||
++proj;
|
||||
assert(proj <= &projections_of_triangles[idx].polygons.back() );
|
||||
|
||||
// a, b are first two points of the polygon for the next layer.
|
||||
proj->add(b);
|
||||
proj->add(a);
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
||||
// Make sure that the output vector can be used.
|
||||
out.resize(layers.size());
|
||||
|
||||
// Now append the collected polygons to respective layers.
|
||||
for (auto& trg : projections_of_triangles) {
|
||||
int layer_id = int(trg.first_layer_id);
|
||||
for (LightPolygon &poly : trg.polygons) {
|
||||
if (layer_id >= int(out.size()))
|
||||
break; // part of triangle could be projected above top layer
|
||||
assert(! poly.pts.empty());
|
||||
// The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well.
|
||||
// if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast<double>()), Vec2d((poly.pts[2] - poly.pts[1]).cast<double>())) < 0)
|
||||
// std::swap(poly.pts.front(), poly.pts.back());
|
||||
|
||||
expolys[layer_id].emplace_back(std::move(poly.pts));
|
||||
++layer_id;
|
||||
}
|
||||
|
||||
out[layer_id].emplace_back(std::move(poly.pts));
|
||||
++layer_id;
|
||||
}
|
||||
|
||||
} // loop over ModelVolumes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrintObject::project_and_append_custom_facets(
|
||||
bool seam, EnforcerBlockerType type, std::vector<Polygons>& out) const
|
||||
{
|
||||
for (const ModelVolume* mv : this->model_object()->volumes)
|
||||
if (mv->is_model_part()) {
|
||||
const indexed_triangle_set custom_facets = seam
|
||||
? mv->seam_facets.get_facets_strict(*mv, type)
|
||||
: mv->supported_facets.get_facets_strict(*mv, type);
|
||||
if (! custom_facets.indices.empty())
|
||||
project_triangles_to_slabs(this->layers(), custom_facets,
|
||||
(Eigen::Translation3d(to_3d(unscaled<double>(this->center_offset()), 0.)) * this->trafo() * mv->get_matrix()).cast<float>(),
|
||||
seam, out);
|
||||
}
|
||||
}
|
||||
|
||||
const Layer* PrintObject::get_layer_at_printz(coordf_t print_z) const {
|
||||
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; });
|
||||
|
@ -39,16 +39,6 @@ LayerPtrs new_layers(
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename LayerContainer>
|
||||
static inline std::vector<float> zs_from_layers(const LayerContainer &layers)
|
||||
{
|
||||
std::vector<float> zs;
|
||||
zs.reserve(layers.size());
|
||||
for (const Layer *l : layers)
|
||||
zs.emplace_back((float)l->slice_z);
|
||||
return zs;
|
||||
}
|
||||
|
||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||
// This function will go away once we get rid of admesh from ModelVolume.
|
||||
static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh)
|
||||
@ -604,14 +594,18 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
||||
if (! layer_split)
|
||||
continue;
|
||||
// Split LayerRegions by by_extruder regions.
|
||||
// layer_range.painted_regions are sorted by extruder ID and parent PrintObject region ID.
|
||||
auto it_painted_region = layer_range.painted_regions.begin();
|
||||
for (int region_id = 0; region_id < int(layer->region_count()); ++ region_id)
|
||||
if (LayerRegion &layerm = *layer->get_region(region_id); ! layerm.slices.surfaces.empty()) {
|
||||
assert(layerm.region().print_object_region_id() == region_id);
|
||||
const BoundingBox bbox = get_extents(layerm.slices.surfaces);
|
||||
assert(it_painted_region < layer_range.painted_regions.end());
|
||||
// Find the first it_painted_region which overrides this region.
|
||||
for (; layer_range.volume_regions[it_painted_region->parent].region->print_object_region_id() < region_id; ++ it_painted_region)
|
||||
assert(it_painted_region < layer_range.painted_regions.end());
|
||||
assert(&layerm.region() == it_painted_region->region && layerm.region().print_object_region_id() == region_id);
|
||||
assert(it_painted_region != layer_range.painted_regions.end());
|
||||
assert(it_painted_region != layer_range.painted_regions.end());
|
||||
assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region());
|
||||
// 1-based extruder ID
|
||||
bool self_trimmed = false;
|
||||
int self_extruder_id = -1;
|
||||
@ -619,7 +613,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
||||
if (ByExtruder &segmented = by_extruder[extruder_id - 1]; segmented.bbox.defined && bbox.overlap(segmented.bbox)) {
|
||||
// Find the target region.
|
||||
for (; int(it_painted_region->extruder_id) < extruder_id; ++ it_painted_region)
|
||||
assert(it_painted_region < layer_range.painted_regions.end());
|
||||
assert(it_painted_region != layer_range.painted_regions.end());
|
||||
assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region() && int(it_painted_region->extruder_id) == extruder_id);
|
||||
//FIXME Don't trim by self, it is not reliable.
|
||||
if (&layerm.region() == it_painted_region->region) {
|
||||
@ -669,7 +663,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
||||
ByRegion &src = by_region[region_id];
|
||||
if (src.needs_merge)
|
||||
// Multiple regions were merged into one.
|
||||
src.expolygons = offset2_ex(src.expolygons, float(scale_(EPSILON)), - float(scale_(EPSILON)));
|
||||
src.expolygons = offset2_ex(src.expolygons, float(scale_(10 * EPSILON)), - float(scale_(10 * EPSILON)));
|
||||
layer->get_region(region_id)->slices.set(std::move(src.expolygons), stInternal);
|
||||
}
|
||||
}
|
||||
@ -701,7 +695,7 @@ void PrintObject::slice_volumes()
|
||||
|
||||
std::vector<float> slice_zs = zs_from_layers(m_layers);
|
||||
Transform3d trafo = this->trafo();
|
||||
trafo.pretranslate(Vec3d(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0));
|
||||
trafo.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0));
|
||||
std::vector<std::vector<ExPolygons>> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs,
|
||||
slice_volumes_inner(
|
||||
print->config(), this->config(), trafo,
|
||||
@ -812,12 +806,12 @@ void PrintObject::slice_volumes()
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
|
||||
std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
|
||||
{
|
||||
auto it_volume = this->model_object()->volumes.begin();
|
||||
auto it_volume_end = this->model_object()->volumes.end();
|
||||
for (; it_volume != it_volume_end && (*it_volume)->type() != model_volume_type; ++ it_volume) ;
|
||||
std::vector<ExPolygons> slices;
|
||||
std::vector<Polygons> slices;
|
||||
if (it_volume != it_volume_end) {
|
||||
// Found at least a single support volume of model_volume_type.
|
||||
std::vector<float> zs = zs_from_layers(this->layers());
|
||||
@ -831,16 +825,18 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
|
||||
for (; it_volume != it_volume_end; ++ it_volume)
|
||||
if ((*it_volume)->type() == model_volume_type) {
|
||||
std::vector<ExPolygons> slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback);
|
||||
if (slices.empty())
|
||||
slices = std::move(slices2);
|
||||
else if (! slices2.empty()) {
|
||||
if (slices.empty()) {
|
||||
slices.reserve(slices2.size());
|
||||
for (ExPolygons &src : slices2)
|
||||
slices.emplace_back(to_polygons(std::move(src)));
|
||||
} else if (!slices2.empty()) {
|
||||
if (merge_layers.empty())
|
||||
merge_layers.assign(zs.size(), false);
|
||||
for (size_t i = 0; i < zs.size(); ++ i) {
|
||||
if (slices[i].empty())
|
||||
slices[i] = std::move(slices2[i]);
|
||||
slices[i] = to_polygons(std::move(slices2[i]));
|
||||
else if (! slices2[i].empty()) {
|
||||
append(slices[i], std::move(slices2[i]));
|
||||
append(slices[i], to_polygons(std::move(slices2[i])));
|
||||
merge_layers[i] = true;
|
||||
merge = true;
|
||||
}
|
||||
@ -848,7 +844,7 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
|
||||
}
|
||||
}
|
||||
if (merge) {
|
||||
std::vector<ExPolygons*> to_merge;
|
||||
std::vector<Polygons*> to_merge;
|
||||
to_merge.reserve(zs.size());
|
||||
for (size_t i = 0; i < zs.size(); ++ i)
|
||||
if (merge_layers[i])
|
||||
@ -857,7 +853,7 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
|
||||
tbb::blocked_range<size_t>(0, to_merge.size()),
|
||||
[&to_merge](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t i = range.begin(); i < range.end(); ++ i)
|
||||
*to_merge[i] = union_ex(*to_merge[i]);
|
||||
*to_merge[i] = union_(*to_merge[i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1353,9 +1353,9 @@ struct SupportAnnotations
|
||||
object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers);
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> enforcers_layers;
|
||||
std::vector<ExPolygons> blockers_layers;
|
||||
const std::vector<Polygons>& buildplate_covered;
|
||||
std::vector<Polygons> enforcers_layers;
|
||||
std::vector<Polygons> blockers_layers;
|
||||
const std::vector<Polygons>& buildplate_covered;
|
||||
};
|
||||
|
||||
struct SlicesMarginCache
|
||||
|
@ -728,7 +728,7 @@ static std::vector<EdgeToFace> create_edge_map(
|
||||
// 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<Vec3i> create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
|
||||
static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
|
||||
{
|
||||
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
|
||||
|
||||
@ -778,14 +778,56 @@ static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its)
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its)
|
||||
{
|
||||
return create_face_neighbors_index_impl(its, [](){});
|
||||
return its_face_edge_ids_impl(its, [](){});
|
||||
}
|
||||
|
||||
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
|
||||
{
|
||||
return create_face_neighbors_index_impl(its, throw_on_cancel_callback);
|
||||
return its_face_edge_ids_impl(its, throw_on_cancel_callback);
|
||||
}
|
||||
|
||||
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges, int *num_edges)
|
||||
{
|
||||
// out elements are not initialized!
|
||||
std::vector<Vec3i> out(face_neighbors.size());
|
||||
int last_edge_id = 0;
|
||||
for (int i = 0; i < int(face_neighbors.size()); ++ i) {
|
||||
const stl_triangle_vertex_indices &triangle = its.indices[i];
|
||||
const Vec3i &neighbors = face_neighbors[i];
|
||||
for (int j = 0; j < 3; ++ j) {
|
||||
int n = neighbors[j];
|
||||
if (n > i) {
|
||||
const stl_triangle_vertex_indices &triangle2 = its.indices[n];
|
||||
int edge_id = last_edge_id ++;
|
||||
Vec2i edge = its_triangle_edge(triangle, j);
|
||||
// First find an edge with opposite orientation.
|
||||
std::swap(edge(0), edge(1));
|
||||
int k = its_triangle_edge_index(triangle2, edge);
|
||||
//FIXME is the following realistic? Could face_neighbors contain such faces?
|
||||
// And if it does, do we want to produce the same edge ID for those mutually incorrectly oriented edges?
|
||||
if (k == -1) {
|
||||
// Second find an edge with the same orientation (the neighbor triangle may be flipped).
|
||||
std::swap(edge(0), edge(1));
|
||||
k = its_triangle_edge_index(triangle2, edge);
|
||||
}
|
||||
assert(k >= 0);
|
||||
out[i](j) = edge_id;
|
||||
out[n](k) = edge_id;
|
||||
} else if (n == -1) {
|
||||
out[i](j) = assign_unbound_edges ? last_edge_id ++ : -1;
|
||||
} else {
|
||||
// Triangle shall never be neighbor of itself.
|
||||
assert(n < i);
|
||||
// Don't do anything, the neighbor will assign us an edge ID in later iterations.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_edges)
|
||||
*num_edges = last_edge_id;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Merge duplicate vertices, return number of vertices removed.
|
||||
@ -1219,14 +1261,14 @@ void VertexFaceIndex::create(const indexed_triangle_set &its)
|
||||
m_vertex_to_face_start.front() = 0;
|
||||
}
|
||||
|
||||
std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its)
|
||||
std::vector<Vec3i> its_face_neighbors(const indexed_triangle_set &its)
|
||||
{
|
||||
return create_neighbors_index(ex_seq, its);
|
||||
return create_face_neighbors_index(ex_seq, its);
|
||||
}
|
||||
|
||||
std::vector<Vec3i> its_create_neighbors_index_par(const indexed_triangle_set &its)
|
||||
std::vector<Vec3i> its_face_neighbors_par(const indexed_triangle_set &its)
|
||||
{
|
||||
return create_neighbors_index(ex_tbb, its);
|
||||
return create_face_neighbors_index(ex_tbb, its);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -116,13 +116,14 @@ private:
|
||||
// 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);
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
|
||||
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);
|
||||
|
||||
// Create index that gives neighbor faces for each face. Ignores face orientations.
|
||||
// TODO: naming...
|
||||
std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its);
|
||||
std::vector<Vec3i> its_create_neighbors_index_par(const indexed_triangle_set &its);
|
||||
std::vector<Vec3i> its_face_neighbors(const indexed_triangle_set &its);
|
||||
std::vector<Vec3i> its_face_neighbors_par(const indexed_triangle_set &its);
|
||||
|
||||
// After applying a transformation with negative determinant, flip the faces to keep the transformed mesh volume positive.
|
||||
void its_flip_triangles(indexed_triangle_set &its);
|
||||
@ -153,6 +154,28 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c
|
||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z);
|
||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transform3f &t, const float z);
|
||||
|
||||
// Index of a vertex inside triangle_indices.
|
||||
inline int its_triangle_vertex_index(const stl_triangle_vertex_indices &triangle_indices, int vertex_idx)
|
||||
{
|
||||
return vertex_idx == triangle_indices[0] ? 0 :
|
||||
vertex_idx == triangle_indices[1] ? 1 :
|
||||
vertex_idx == triangle_indices[2] ? 2 : -1;
|
||||
}
|
||||
|
||||
inline Vec2i its_triangle_edge(const stl_triangle_vertex_indices &triangle_indices, int edge_idx)
|
||||
{
|
||||
int next_edge_idx = (edge_idx == 2) ? 0 : edge_idx + 1;
|
||||
return { triangle_indices[edge_idx], triangle_indices[next_edge_idx] };
|
||||
}
|
||||
|
||||
// Index of an edge inside triangle.
|
||||
inline int its_triangle_edge_index(const stl_triangle_vertex_indices &triangle_indices, const Vec2i &triangle_edge)
|
||||
{
|
||||
return triangle_edge(0) == triangle_indices[0] && triangle_edge(1) == triangle_indices[1] ? 0 :
|
||||
triangle_edge(0) == triangle_indices[1] && triangle_edge(1) == triangle_indices[2] ? 1 :
|
||||
triangle_edge(0) == triangle_indices[2] && triangle_edge(1) == triangle_indices[0] ? 2 : -1;
|
||||
}
|
||||
|
||||
using its_triangle = std::array<stl_vertex, 3>;
|
||||
|
||||
inline its_triangle its_triangle_vertices(const indexed_triangle_set &its,
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Tesselate.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "TriangleMeshSlicer.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -14,6 +15,10 @@
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
// #define EXPENSIVE_DEBUG_CHECKS
|
||||
#endif // NDEBUG
|
||||
|
||||
#if 0
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
@ -26,6 +31,8 @@
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
|
||||
// #define SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
|
||||
#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
|
||||
#include "SVG.hpp"
|
||||
#endif
|
||||
@ -64,6 +71,8 @@ public:
|
||||
|
||||
bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
|
||||
void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
|
||||
|
||||
void reverse() { std::swap(a, b); std::swap(a_id, b_id); std::swap(edge_a_id, edge_b_id); }
|
||||
|
||||
// Inherits Point a, b
|
||||
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
|
||||
@ -81,8 +90,14 @@ public:
|
||||
Top,
|
||||
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
|
||||
Bottom,
|
||||
// Two vertices are aligned with the cutting plane, the edge is shared by two triangles, where one
|
||||
// triangle is below or at the cutting plane and the other is above or at the cutting plane (only one
|
||||
// vertex may lie on the plane).
|
||||
TopBottom,
|
||||
// All three vertices of a face are aligned with the cutting plane.
|
||||
Horizontal
|
||||
Horizontal,
|
||||
// Edge
|
||||
Slab,
|
||||
};
|
||||
|
||||
// feGeneral, feTop, feBottom, feHorizontal
|
||||
@ -102,6 +117,15 @@ public:
|
||||
SKIP = 0x200,
|
||||
};
|
||||
uint32_t flags { 0 };
|
||||
|
||||
#ifndef NDEBUG
|
||||
enum class Source {
|
||||
BottomPlane,
|
||||
TopPlane,
|
||||
Slab,
|
||||
};
|
||||
Source source { Source::BottomPlane };
|
||||
#endif // NDEBUG
|
||||
};
|
||||
|
||||
using IntersectionLines = std::vector<IntersectionLine>;
|
||||
@ -119,7 +143,7 @@ static FacetSliceType slice_facet(
|
||||
// 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 Vec3i &edge_ids,
|
||||
const int idx_vertex_lowest,
|
||||
const bool horizontal,
|
||||
IntersectionLine &line_out)
|
||||
@ -138,7 +162,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_ids(k);
|
||||
a_id = indices[k];
|
||||
a = vertices + k;
|
||||
b_id = indices[l];
|
||||
@ -284,11 +308,11 @@ void slice_facet_at_zs(
|
||||
const std::vector<Vec3f> &mesh_vertices,
|
||||
const TransformVertex &transform_vertex_fn,
|
||||
const stl_triangle_vertex_indices &indices,
|
||||
const Vec3i &facet_neighbors,
|
||||
const Vec3i &edge_ids,
|
||||
// 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)
|
||||
std::array<std::mutex, 64> &lines_mutex)
|
||||
{
|
||||
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)]) };
|
||||
|
||||
@ -299,43 +323,439 @@ void slice_facet_at_zs(
|
||||
// find layer extents
|
||||
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
|
||||
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
|
||||
|
||||
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 &&
|
||||
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 - zs.begin()].emplace_back(il);
|
||||
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
|
||||
if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
|
||||
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
||||
size_t slice_id = it - zs.begin();
|
||||
boost::lock_guard<std::mutex> l(lines_mutex[slice_id >> 6]);
|
||||
lines[slice_id].emplace_back(il);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TransformVertex, typename ThrowOnCancel>
|
||||
inline std::vector<IntersectionLines> slice_make_lines(
|
||||
static 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<Vec3i> &face_edge_ids,
|
||||
const std::vector<float> &zs,
|
||||
const ThrowOnCancel throw_on_cancel_fn)
|
||||
{
|
||||
std::vector<IntersectionLines> lines(zs.size(), IntersectionLines());
|
||||
boost::mutex lines_mutex;
|
||||
std::array<std::mutex, 64> 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) {
|
||||
[&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &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);
|
||||
slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], zs, lines, lines_mutex);
|
||||
}
|
||||
}
|
||||
);
|
||||
return lines;
|
||||
}
|
||||
|
||||
// For projecting triangle sets onto slice slabs.
|
||||
struct SlabLines {
|
||||
// Intersection lines of a slice with a triangle set, CCW oriented.
|
||||
std::vector<IntersectionLines> at_slice;
|
||||
// Projections of triangle set boundary lines into layer below (for projection from the top)
|
||||
// or into layer above (for projection from the bottom).
|
||||
// In both cases the intersection liens are CCW oriented.
|
||||
std::vector<IntersectionLines> between_slices;
|
||||
};
|
||||
|
||||
// Orientation of the face normal in regard to a XY plane pointing upwards.
|
||||
enum class FaceOrientation : char {
|
||||
// Z component of the normal is positive.
|
||||
Up,
|
||||
// Z component of the normal is negative.
|
||||
Down,
|
||||
// Z component of the normal is zero.
|
||||
Vertical,
|
||||
// Triangle is degenerate, thus its normal is undefined. We may want to slice the degenerate triangles
|
||||
// because of the connectivity information they carry.
|
||||
Degenerate
|
||||
};
|
||||
|
||||
template<bool ProjectionFromTop>
|
||||
void slice_facet_with_slabs(
|
||||
// Scaled or unscaled vertices. transform_vertex_fn may scale zs.
|
||||
const std::vector<Vec3f> &mesh_vertices,
|
||||
const std::vector<stl_triangle_vertex_indices> &mesh_triangles,
|
||||
const size_t facet_idx,
|
||||
const Vec3i &facet_neighbors,
|
||||
const Vec3i &facet_edge_ids,
|
||||
// Increase edge_ids at the top plane of the slab edges by num_edges to allow chaining
|
||||
// from bottom plane of the slab to the top plane of the slab and vice versa.
|
||||
const int num_edges,
|
||||
const std::vector<float> &zs,
|
||||
SlabLines &lines,
|
||||
std::array<std::mutex, 64> &lines_mutex)
|
||||
{
|
||||
const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx];
|
||||
stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], 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()));
|
||||
const bool horizontal = min_z == max_z;
|
||||
|
||||
// find layer extents
|
||||
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
|
||||
assert(min_layer == zs.end() ? max_layer == zs.end() : *min_layer >= min_z);
|
||||
assert(max_layer == zs.end() || *max_layer > max_z);
|
||||
|
||||
auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) {
|
||||
if (reverse)
|
||||
il.reverse();
|
||||
boost::lock_guard<std::mutex> l(lines_mutex[(slab_id + 32) >> 6]);
|
||||
lines.between_slices[slab_id].emplace_back(il);
|
||||
};
|
||||
|
||||
if (min_layer == max_layer || horizontal) {
|
||||
// Horizontal face or a nearly horizontal face that fits between two layers or below the bottom most or above the top most layer.
|
||||
assert(horizontal || zs.empty() || max_z < zs.front() || min_z > zs.back() ||
|
||||
(min_layer == max_layer && min_layer != zs.end() && min_layer != zs.begin() && *(min_layer - 1) < min_z && *min_layer > max_z));
|
||||
if (horizontal && min_layer != zs.end() && *min_layer == min_z) {
|
||||
// Slicing a horizontal triangle with a slicing plane. The triangle has to be upwards facing for ProjectionFromTop
|
||||
// and downwards facing for ! ProjectionFromTop.
|
||||
assert(min_layer != max_layer);
|
||||
size_t line_id = min_layer - zs.begin();
|
||||
for (int iedge = 0; iedge < 3; ++ iedge)
|
||||
if (facet_neighbors(iedge) == -1) {
|
||||
int i = iedge;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
assert(vertices[i].z() == zs[line_id]);
|
||||
assert(vertices[j].z() == zs[line_id]);
|
||||
IntersectionLine il {
|
||||
{ to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
|
||||
indices(i), indices(j), -1, -1,
|
||||
ProjectionFromTop ? IntersectionLine::FacetEdgeType::Bottom : IntersectionLine::FacetEdgeType::Top
|
||||
};
|
||||
// Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining.
|
||||
// if (! ProjectionFromTop) il.reverse();
|
||||
boost::lock_guard<std::mutex> l(lines_mutex[line_id >> 6]);
|
||||
lines.at_slice[line_id].emplace_back(il);
|
||||
}
|
||||
} else {
|
||||
// Triangle is completely between two slicing planes, the triangle may or may not be horizontal, which
|
||||
// does not matter for the processing of such a triangle.
|
||||
size_t slab_id;
|
||||
if (ProjectionFromTop) {
|
||||
if (max_layer == zs.begin()) {
|
||||
// Not slicing the triangle and it is below the lowest layer.
|
||||
return;
|
||||
} else {
|
||||
// Not slicing the triangle and it could be projected into a slab.
|
||||
slab_id = max_layer - zs.begin();
|
||||
}
|
||||
} else {
|
||||
// projection from bottom
|
||||
if (min_layer == zs.end()) {
|
||||
// Not slicing the triangle and it is above the highest layer.
|
||||
return;
|
||||
} else {
|
||||
// Not slicing the triangle and it could be projected into a slab.
|
||||
slab_id = min_layer - zs.begin();
|
||||
}
|
||||
}
|
||||
if (ProjectionFromTop)
|
||||
-- slab_id;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge)
|
||||
if (facet_neighbors(iedge) == -1) {
|
||||
int i = iedge;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
assert(ProjectionFromTop ? vertices[i].z() >= zs[slab_id] : vertices[i].z() <= zs[slab_id]);
|
||||
assert(ProjectionFromTop ? vertices[j].z() >= zs[slab_id] : vertices[j].z() <= zs[slab_id]);
|
||||
emit_slab_edge(
|
||||
IntersectionLine {
|
||||
{ to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
|
||||
indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab
|
||||
},
|
||||
slab_id, ! ProjectionFromTop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The triangle is not horizontal and at least a single slicing plane intersects the triangle.
|
||||
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
|
||||
IntersectionLine il_prev;
|
||||
for (auto it = min_layer; it != max_layer; ++ it) {
|
||||
IntersectionLine il;
|
||||
auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, il);
|
||||
if (type == FacetSliceType::NoSlice) {
|
||||
// One and exactly one vertex is touching the slicing plane.
|
||||
} else {
|
||||
if (il.edge_type == IntersectionLine::FacetEdgeType::Top || il.edge_type == IntersectionLine::FacetEdgeType::Bottom) {
|
||||
// The non-horizontal triangle is being sliced at one of its edges.
|
||||
// If the edge is open (it does not have a neighbor), add it.
|
||||
// If the edge has a neighbor, then add it as TopBottom, and do it just once.
|
||||
assert(il.a_id != -1 && il.b_id != -1);
|
||||
assert(il.edge_a_id == -1 && il.edge_b_id == -1);
|
||||
// Identify edge ID from the edge vertices.
|
||||
int edge_id;
|
||||
if (type == FacetSliceType::Cutting) {
|
||||
// The edge is oriented CCW along the face perimeter.
|
||||
assert(il.edge_type == IntersectionLine::FacetEdgeType::Bottom);
|
||||
edge_id = il.a_id == indices(0) ? 0 : il.a_id == indices(1) ? 1 : 2;
|
||||
assert(il.a_id == indices(edge_id));
|
||||
assert(il.b_id == indices(next_idx_modulo(edge_id, 3)));
|
||||
} else {
|
||||
// The edge is oriented CW along the face perimeter.
|
||||
assert(il.edge_type == IntersectionLine::FacetEdgeType::Top);
|
||||
edge_id = il.b_id == indices(0) ? 0 : il.b_id == indices(1) ? 1 : 2;
|
||||
assert(il.b_id == indices(edge_id));
|
||||
assert(il.a_id == indices(next_idx_modulo(edge_id, 3)));
|
||||
}
|
||||
int neighbor_idx = facet_neighbors(edge_id);
|
||||
if (neighbor_idx == -1) {
|
||||
// Save the open edge for sure.
|
||||
type = FacetSliceType::Slicing;
|
||||
} else {
|
||||
const stl_triangle_vertex_indices &neighbor = mesh_triangles[neighbor_idx];
|
||||
float z = *it;
|
||||
#ifndef NDEBUG
|
||||
int num_on_plane = (mesh_vertices[neighbor(0)].z() == z) + (mesh_vertices[neighbor(1)].z() == z) + (mesh_vertices[neighbor(2)].z() == z);
|
||||
assert(num_on_plane == 2 || num_on_plane == 3);
|
||||
#endif // NDEBUG
|
||||
if (mesh_vertices[neighbor(0)].z() == z && mesh_vertices[neighbor(1)].z() == z && mesh_vertices[neighbor(2)].z() == z) {
|
||||
// The neighbor triangle is horizontal.
|
||||
// Is the corner convex or concave?
|
||||
if (il.edge_type == (ProjectionFromTop ? IntersectionLine::FacetEdgeType::Top : IntersectionLine::FacetEdgeType::Bottom)) {
|
||||
// Convex corner. Add this edge to both slabs, the edge is a boundary edge of both the projection patch below and
|
||||
// above this slicing plane.
|
||||
type = FacetSliceType::Slicing;
|
||||
il.edge_type = IntersectionLine::FacetEdgeType::TopBottom;
|
||||
} else {
|
||||
// Concave corner. Ignore this edge, it is internal to the projection patch.
|
||||
type = FacetSliceType::Cutting;
|
||||
}
|
||||
} else if (il.edge_type == IntersectionLine::FacetEdgeType::Top) {
|
||||
// Indicate that the edge belongs to both the slab below and above the plane.
|
||||
assert(type == FacetSliceType::Slicing);
|
||||
il.edge_type = IntersectionLine::FacetEdgeType::TopBottom;
|
||||
} else {
|
||||
// Don't add this edge, as the neighbor triangle will add the same edge as FacetEdgeType::TopBottom.
|
||||
assert(type == FacetSliceType::Cutting);
|
||||
assert(il.edge_type == IntersectionLine::FacetEdgeType::Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == FacetSliceType::Slicing) {
|
||||
if (! ProjectionFromTop)
|
||||
il.reverse();
|
||||
size_t line_id = it - zs.begin();
|
||||
boost::lock_guard<std::mutex> l(lines_mutex[line_id >> 6]);
|
||||
lines.at_slice[line_id].emplace_back(il);
|
||||
}
|
||||
}
|
||||
if (! ProjectionFromTop || it != zs.begin()) {
|
||||
size_t slab_id = it - zs.begin();
|
||||
if (ProjectionFromTop)
|
||||
-- slab_id;
|
||||
// Try to project unbound edges.
|
||||
for (int iedge = 0; iedge < 3; ++ iedge)
|
||||
if (facet_neighbors(iedge) == -1) {
|
||||
// Unbound edge.
|
||||
int edge_id = facet_edge_ids(iedge);
|
||||
bool intersects_this = il.edge_a_id == edge_id || il.edge_b_id == edge_id;
|
||||
bool intersects_prev = il_prev.edge_a_id == edge_id || il_prev.edge_b_id == edge_id;
|
||||
int i = iedge;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
assert((! intersects_this && ! intersects_prev) || vertices[j].z() != vertices[i].z());
|
||||
bool edge_up = vertices[j].z() > vertices[i].z();
|
||||
if (intersects_this && intersects_prev) {
|
||||
// Intersects both, emit the segment between these intersections.
|
||||
Line l(il_prev.edge_a_id == edge_id ? il_prev.a : il_prev.b,
|
||||
il.edge_a_id == edge_id ? il.a : il.b);
|
||||
emit_slab_edge(
|
||||
IntersectionLine { l, -1, -1, edge_id, edge_id + num_edges, IntersectionLine::FacetEdgeType::Slab },
|
||||
slab_id, ProjectionFromTop != edge_up);
|
||||
} else if (intersects_this) {
|
||||
// Intersects just the top plane, may touch the bottom plane.
|
||||
assert((vertices[i].z() > *it && vertices[j].z() < *it) || (vertices[i].z() < *it && vertices[j].z() > *it));
|
||||
assert(il.edge_a_id == edge_id || il.edge_b_id == edge_id);
|
||||
emit_slab_edge(
|
||||
IntersectionLine { {
|
||||
to_2d(edge_up ? vertices[i] : vertices[j]).cast<coord_t>(),
|
||||
il.edge_a_id == edge_id ? il.a : il.b
|
||||
},
|
||||
edge_up ? indices(i) : indices(j), -1, -1, edge_id + num_edges, IntersectionLine::FacetEdgeType::Slab
|
||||
},
|
||||
slab_id, ProjectionFromTop != edge_up);
|
||||
} else if (intersects_prev) {
|
||||
// Intersects just the bottom plane, may touch the top vertex.
|
||||
assert(*it <= max_z);
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
auto it_prev = it;
|
||||
-- it_prev;
|
||||
assert((vertices[i].z() > *it_prev && vertices[j].z() < *it_prev) || (vertices[i].z() < *it_prev && vertices[j].z() > *it_prev));
|
||||
}
|
||||
#endif // NDEBUG
|
||||
emit_slab_edge(
|
||||
IntersectionLine { {
|
||||
il_prev.edge_a_id == edge_id ? il_prev.a : il_prev.b,
|
||||
to_2d(edge_up ? vertices[j] : vertices[i]).cast<coord_t>()
|
||||
},
|
||||
-1, edge_up ? indices(j) : indices(i), edge_id, -1, IntersectionLine::FacetEdgeType::Slab
|
||||
},
|
||||
slab_id, ProjectionFromTop != edge_up);
|
||||
} else if (float zi = vertices[i].z(), zj = vertices[j].z(); zi < *it || zj < *it) {
|
||||
// The edge does not intersect the current plane and it does not intersect the previous plane either.
|
||||
// Both points have to be inside the slab.
|
||||
assert(zi <= *it && zj <= *it);
|
||||
#ifndef NDEBUG
|
||||
if (type == FacetSliceType::Slicing || type == FacetSliceType::Cutting) {
|
||||
// Such edge should already be processed in the code above, it shall be skipped here.
|
||||
assert(indices(i) != il.b_id || indices(j) != il.a_id);
|
||||
assert(indices(i) != il.a_id || indices(j) != il.b_id);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
// Is it inside the slab?
|
||||
bool inside_slab = true;
|
||||
if (it != min_layer) {
|
||||
auto it_prev = it;
|
||||
-- it_prev;
|
||||
assert(*it_prev >= *min_layer && *it_prev < *it);
|
||||
// One point may touch the plane below, the other must not.
|
||||
inside_slab = zi > *it_prev || zj > *it_prev;
|
||||
// Both points have to be inside the slab.
|
||||
assert(! inside_slab || (zi >= *it_prev && zj >= *it_prev));
|
||||
}
|
||||
if (inside_slab) {
|
||||
assert(ProjectionFromTop ? vertices[i].z() >= zs[slab_id] : vertices[i].z() <= zs[slab_id]);
|
||||
assert(ProjectionFromTop ? vertices[j].z() >= zs[slab_id] : vertices[j].z() <= zs[slab_id]);
|
||||
emit_slab_edge(
|
||||
IntersectionLine {
|
||||
{ to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
|
||||
indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab
|
||||
},
|
||||
slab_id, ! ProjectionFromTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
il_prev = il;
|
||||
}
|
||||
if (ProjectionFromTop || max_layer != zs.end()) {
|
||||
// Try to project unbound edges above the last slicing plane to the last slab.
|
||||
// Last layer slicing this triangle.
|
||||
auto it = max_layer - 1;
|
||||
size_t slab_id = max_layer - zs.begin();
|
||||
if (ProjectionFromTop)
|
||||
-- slab_id;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge)
|
||||
if (facet_neighbors(iedge) == -1) {
|
||||
// Unbound edge.
|
||||
int edge_id = facet_edge_ids(iedge);
|
||||
int i = iedge;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
if (il_prev.edge_a_id == edge_id || il_prev.edge_b_id == edge_id) {
|
||||
// Intersects just the bottom plane, may touch the top vertex.
|
||||
assert((vertices[i].z() > *it && vertices[j].z() < *it) || (vertices[i].z() < *it && vertices[j].z() > *it));
|
||||
bool edge_up = vertices[j].z() > vertices[i].z();
|
||||
emit_slab_edge(
|
||||
IntersectionLine{ {
|
||||
il_prev.edge_a_id == edge_id ? il_prev.a : il_prev.b,
|
||||
to_2d(edge_up ? vertices[j] : vertices[i]).cast<coord_t>()
|
||||
},
|
||||
-1, edge_up ? indices(j) : indices(i), edge_id, -1, IntersectionLine::FacetEdgeType::Slab
|
||||
},
|
||||
slab_id, ProjectionFromTop != edge_up);
|
||||
} else if (float zi = vertices[i].z(), zj = vertices[j].z(); zi > *it || zj > *it) {
|
||||
// The edge does not intersect the current plane and it does not intersect the previous plane either.
|
||||
// Both points have to be inside the slab.
|
||||
assert(zi >= *it && zj >= *it);
|
||||
assert(max_layer == zs.end() || (zi < *max_layer && zj < *max_layer));
|
||||
emit_slab_edge(
|
||||
IntersectionLine{
|
||||
{ to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
|
||||
indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab
|
||||
},
|
||||
slab_id, ! ProjectionFromTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used by slice_mesh_slabs() to produce on-slice lines and between-slices lines.
|
||||
// Returning top / bottom SlabLines.
|
||||
template<typename ThrowOnCancel>
|
||||
inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
|
||||
const std::vector<stl_vertex> &vertices,
|
||||
const std::vector<stl_triangle_vertex_indices> &indices,
|
||||
const std::vector<Vec3i> &face_neighbors,
|
||||
const std::vector<Vec3i> &face_edge_ids,
|
||||
// Total number of edges. All face_edge_ids are lower than num_edges.
|
||||
// num_edges will be used to distinguish between intersections with the top and bottom plane.
|
||||
const int num_edges,
|
||||
const std::vector<FaceOrientation> &face_orientation,
|
||||
const std::vector<float> &zs,
|
||||
bool top,
|
||||
bool bottom,
|
||||
const ThrowOnCancel throw_on_cancel_fn)
|
||||
{
|
||||
std::pair<SlabLines, SlabLines> out;
|
||||
SlabLines &lines_top = out.first;
|
||||
SlabLines &lines_bottom = out.second;
|
||||
std::array<std::mutex, 64> lines_mutex_top;
|
||||
std::array<std::mutex, 64> lines_mutex_bottom;
|
||||
|
||||
if (top) {
|
||||
lines_top.at_slice.assign(zs.size(), IntersectionLines());
|
||||
lines_top.between_slices.assign(zs.size(), IntersectionLines());
|
||||
}
|
||||
if (bottom) {
|
||||
lines_bottom.at_slice.assign(zs.size(), IntersectionLines());
|
||||
lines_bottom.between_slices.assign(zs.size(), IntersectionLines());
|
||||
}
|
||||
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<int>(0, int(indices.size())),
|
||||
[&vertices, &indices, &face_neighbors, &face_edge_ids, num_edges, &face_orientation, &zs, top, bottom, &lines_top, &lines_bottom, &lines_mutex_top, &lines_mutex_bottom, 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();
|
||||
FaceOrientation fo = face_orientation[face_idx];
|
||||
Vec3i edge_ids = face_edge_ids[face_idx];
|
||||
if (top && (fo == FaceOrientation::Up || fo == FaceOrientation::Degenerate)) {
|
||||
Vec3i neighbors = face_neighbors[face_idx];
|
||||
// Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
if (neighbors(i) != -1) {
|
||||
FaceOrientation fo2 = face_orientation[neighbors(i)];
|
||||
if (fo2 != FaceOrientation::Up && fo2 != FaceOrientation::Degenerate)
|
||||
neighbors(i) = -1;
|
||||
}
|
||||
slice_facet_with_slabs<true>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top);
|
||||
}
|
||||
if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) {
|
||||
Vec3i neighbors = face_neighbors[face_idx];
|
||||
// Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
if (neighbors(i) != -1) {
|
||||
FaceOrientation fo2 = face_orientation[neighbors(i)];
|
||||
if (fo2 != FaceOrientation::Down && fo2 != FaceOrientation::Degenerate)
|
||||
neighbors(i) = -1;
|
||||
}
|
||||
slice_facet_with_slabs<false>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_bottom, lines_mutex_bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return out;
|
||||
}
|
||||
|
||||
#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.
|
||||
@ -495,6 +915,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
|
||||
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
assert(first_line->a == last_line->b);
|
||||
loops.emplace_back(std::move(loop_pts));
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||
@ -513,6 +934,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
|
||||
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
|
||||
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
|
||||
*/
|
||||
assert(last_line->b == next_line->a);
|
||||
loop_pts.emplace_back(next_line->a);
|
||||
last_line = next_line;
|
||||
next_line->set_skip();
|
||||
@ -778,7 +1200,7 @@ static Polygons make_loops(
|
||||
{
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
|
||||
svg.draw(union_ex(*loops));
|
||||
svg.draw(union_ex(loops));
|
||||
for (const OpenPolyline &pl : open_polylines)
|
||||
svg.draw(Polyline(pl.points), "red");
|
||||
svg.Close();
|
||||
@ -795,7 +1217,7 @@ static Polygons make_loops(
|
||||
{
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
|
||||
svg.draw(union_ex(*loops));
|
||||
svg.draw(union_ex(loops));
|
||||
for (const OpenPolyline &pl : open_polylines) {
|
||||
if (pl.points.empty())
|
||||
continue;
|
||||
@ -825,7 +1247,7 @@ static Polygons make_loops(
|
||||
{
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
|
||||
svg.draw(union_ex(*loops));
|
||||
svg.draw(union_ex(loops));
|
||||
for (const OpenPolyline &pl : open_polylines) {
|
||||
if (pl.points.empty())
|
||||
continue;
|
||||
@ -892,6 +1314,132 @@ static std::vector<Polygons> make_loops(
|
||||
return layers;
|
||||
}
|
||||
|
||||
// used by slice_mesh_slabs() to produce loops from on-slice lines and between-slices lines.
|
||||
template<bool ProjectionFromTop, typename ThrowOnCancel>
|
||||
static std::vector<Polygons> make_slab_loops(
|
||||
// Lines will have their flags modified.
|
||||
SlabLines &lines,
|
||||
// To differentiate edge IDs of the top plane from the edge IDs of the bottom plane for chaining.
|
||||
int num_edges,
|
||||
ThrowOnCancel throw_on_cancel)
|
||||
{
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
static int iRun = 0;
|
||||
++ iRun;
|
||||
#endif // SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
|
||||
assert(! lines.at_slice.empty() && lines.at_slice.size() == lines.between_slices.size());
|
||||
std::vector<Polygons> layers;
|
||||
layers.resize(lines.at_slice.size());
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<int>(0, int(lines.at_slice.size())),
|
||||
[&lines, num_edges, &layers, throw_on_cancel](const tbb::blocked_range<int> &range) {
|
||||
for (int line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
|
||||
if ((line_idx & 0x0ffff) == 0)
|
||||
throw_on_cancel();
|
||||
IntersectionLines in;
|
||||
size_t nlines = lines.between_slices[line_idx].size();
|
||||
int slice_below = ProjectionFromTop ? line_idx : line_idx - 1;
|
||||
int slice_above = ProjectionFromTop ? line_idx + 1 : line_idx;
|
||||
bool has_slice_below = ProjectionFromTop || line_idx > 0;
|
||||
bool has_slice_above = ! ProjectionFromTop || line_idx + 1 < int(lines.at_slice.size());
|
||||
if (has_slice_below)
|
||||
nlines += lines.at_slice[slice_below].size();
|
||||
if (has_slice_above)
|
||||
nlines += lines.at_slice[slice_above].size();
|
||||
if (nlines) {
|
||||
in.reserve(nlines);
|
||||
if (has_slice_below) {
|
||||
for (const IntersectionLine &l : lines.at_slice[slice_below])
|
||||
if (l.edge_type != IntersectionLine::FacetEdgeType::Top) {
|
||||
in.emplace_back(l);
|
||||
#ifndef NDEBUG
|
||||
in.back().source = IntersectionLine::Source::BottomPlane;
|
||||
#endif // NDEBUG
|
||||
}
|
||||
}
|
||||
{
|
||||
// Edges in between slice_below and slice_above.
|
||||
#ifndef NDEBUG
|
||||
size_t old_size = in.size();
|
||||
#endif // NDEBUG
|
||||
// Edge IDs of end points on in-between lines that touch the layer above are already increased with num_edges.
|
||||
append(in, lines.between_slices[line_idx]);
|
||||
#ifndef NDEBUG
|
||||
for (auto it = in.begin() + old_size; it != in.end(); ++ it) {
|
||||
assert(it->edge_type == IntersectionLine::FacetEdgeType::Slab);
|
||||
it->source = IntersectionLine::Source::Slab;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
if (has_slice_above) {
|
||||
for (const IntersectionLine &lsrc : lines.at_slice[slice_above])
|
||||
if (lsrc.edge_type != IntersectionLine::FacetEdgeType::Bottom) {
|
||||
in.emplace_back(lsrc);
|
||||
auto &l = in.back();
|
||||
l.reverse();
|
||||
// Differentiate edge IDs of the top plane from the edge IDs of the bottom plane for chaining.
|
||||
if (l.edge_a_id >= 0)
|
||||
l.edge_a_id += num_edges;
|
||||
if (l.edge_b_id >= 0)
|
||||
l.edge_b_id += num_edges;
|
||||
#ifndef NDEBUG
|
||||
l.source = IntersectionLine::Source::TopPlane;
|
||||
#endif // NDEBUG
|
||||
}
|
||||
}
|
||||
if (! in.empty()) {
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
BoundingBox bbox_svg;
|
||||
coordf_t stroke_width = scale_(0.02);
|
||||
{
|
||||
for (const IntersectionLine &line : in) {
|
||||
bbox_svg.merge(line.a);
|
||||
bbox_svg.merge(line.b);
|
||||
}
|
||||
SVG svg(debug_out_path("make_slab_loops-in-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
|
||||
svg.arrows = true;
|
||||
for (const IntersectionLine& line : in) {
|
||||
const char* color = line.source == IntersectionLine::Source::BottomPlane ? "red" : line.source == IntersectionLine::Source::TopPlane ? "blue" : "green";
|
||||
svg.draw(line, color, stroke_width);
|
||||
}
|
||||
svg.Close();
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
Polygons &loops = layers[line_idx];
|
||||
std::vector<OpenPolyline> open_polylines;
|
||||
chain_lines_by_triangle_connectivity(in, loops, open_polylines);
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
SVG svg(debug_out_path("make_slab_loops-out-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
|
||||
svg.arrows = true;
|
||||
for (const IntersectionLine& line : in) {
|
||||
const char* color = line.source == IntersectionLine::Source::BottomPlane ? "red" : line.source == IntersectionLine::Source::TopPlane ? "blue" : "green";
|
||||
svg.draw(line, color, stroke_width);
|
||||
}
|
||||
svg.draw(loops, "black");
|
||||
svg.Close();
|
||||
}
|
||||
{
|
||||
SVG svg(debug_out_path("make_slab_loops-open-polylines-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
|
||||
svg.draw(loops, "black");
|
||||
svg.arrows = true;
|
||||
for (const OpenPolyline &open_polyline : open_polylines)
|
||||
svg.draw(Polyline(open_polyline.points), "black", stroke_width);
|
||||
svg.Close();
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
assert(! loops.empty());
|
||||
assert(open_polylines.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
// Used to cut the mesh into two halves.
|
||||
static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines)
|
||||
{
|
||||
@ -1055,6 +1603,44 @@ static void make_expolygons(const Polygons &loops, const float closing_radius, c
|
||||
union_ex(loops, fill_type));
|
||||
}
|
||||
|
||||
// Make a trafo for transforming the vertices. Scale up in XY, not in Z.
|
||||
static inline Transform3f make_trafo_for_slicing(const Transform3d &trafo)
|
||||
{
|
||||
auto t = trafo;
|
||||
static constexpr const double s = 1. / SCALING_FACTOR;
|
||||
t.prescale(Vec3d(s, s, 1.));
|
||||
return t.cast<float>();
|
||||
}
|
||||
|
||||
static inline bool is_identity(const Transform3d &trafo)
|
||||
{
|
||||
return trafo.matrix() == Transform3d::Identity().matrix();
|
||||
}
|
||||
|
||||
static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed_triangle_set &mesh, const Transform3d &trafo)
|
||||
{
|
||||
// Copy and scale vertices in XY, don't scale in Z.
|
||||
// Possibly apply the transformation.
|
||||
static constexpr const double s = 1. / SCALING_FACTOR;
|
||||
std::vector<stl_vertex> out(mesh.vertices);
|
||||
if (is_identity(trafo)) {
|
||||
// Identity.
|
||||
for (stl_vertex &v : out) {
|
||||
// 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 = trafo;
|
||||
t.prescale(Vec3d(s, s, 1.));
|
||||
auto tf = t.cast<float>();
|
||||
for (stl_vertex &v : out)
|
||||
v = tf * v;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Polygons> slice_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
// Unscaled Zs
|
||||
@ -1071,41 +1657,23 @@ std::vector<Polygons> slice_mesh(
|
||||
// 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;
|
||||
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh);
|
||||
if (zs.size() <= 1) {
|
||||
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
|
||||
if (identity)
|
||||
if (is_identity(params.trafo)) {
|
||||
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>();
|
||||
lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, facets_edges, zs, throw_on_cancel);
|
||||
mesh.indices, face_edge_ids, zs, throw_on_cancel);
|
||||
} else {
|
||||
// Transform the vertices, scale up in XY, not in Z.
|
||||
Transform3f tf = make_trafo_for_slicing(params.trafo);
|
||||
lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, 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);
|
||||
// Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation.
|
||||
lines = slice_make_lines(
|
||||
transform_mesh_vertices_for_slicing(mesh, params.trafo),
|
||||
[](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1194,6 +1762,107 @@ std::vector<ExPolygons> slice_mesh_ex(
|
||||
return layers;
|
||||
}
|
||||
|
||||
// Slice a triangle set with a set of Z slabs (thick layers).
|
||||
// The effect is similar to producing the usual top / bottom layers from a sliced mesh by
|
||||
// subtracting layer[i] from layer[i - 1] for the top surfaces resp.
|
||||
// subtracting layer[i] from layer[i + 1] for the bottom surfaces,
|
||||
// with the exception that the triangle set this function processes may not cover the whole top resp. bottom surface.
|
||||
// top resp. bottom surfaces are calculated only if out_top resp. out_bottom is not null.
|
||||
void slice_mesh_slabs(
|
||||
const indexed_triangle_set &mesh,
|
||||
// Unscaled Zs
|
||||
const std::vector<float> &zs,
|
||||
const Transform3d &trafo,
|
||||
std::vector<Polygons> *out_top,
|
||||
std::vector<Polygons> *out_bottom,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons";
|
||||
|
||||
#ifdef EXPENSIVE_DEBUG_CHECKS
|
||||
{
|
||||
// Verify that the vertices are unique.
|
||||
auto v = mesh.vertices;
|
||||
std::sort(v.begin(), v.end(), [](auto &l, auto &r) {
|
||||
return l.x() < r.x() || (l.x() == r.x() && (l.y() < r.y() || (l.y() == r.y() && l.z() < r.z())));
|
||||
});
|
||||
size_t num_duplicates = v.end() - std::unique(v.begin(), v.end());
|
||||
assert(num_duplicates == 0);
|
||||
}
|
||||
if (0)
|
||||
{
|
||||
// Verify that there are no T-joints.
|
||||
// The T-joints could likely be already part of the source mesh.
|
||||
for (const auto &tri : mesh.indices)
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
int j = next_idx_modulo(i, 3);
|
||||
int k = next_idx_modulo(j, 3);
|
||||
auto &v1 = mesh.vertices[tri(i)];
|
||||
auto &v2 = mesh.vertices[tri(j)];
|
||||
auto &v3 = mesh.vertices[tri(k)];
|
||||
for (auto &pt : mesh.vertices)
|
||||
if (&pt != &v1 && &pt != &v2) {
|
||||
assert(pt != v1 && pt != v2);
|
||||
assert((pt - v1).norm() > EPSILON);
|
||||
assert((pt - v2).norm() > EPSILON);
|
||||
auto l2 = (v2 - v1).squaredNorm();
|
||||
assert(l2 > 0);
|
||||
auto t = (pt - v1).dot(v2 - v1);
|
||||
if (t > 0 && t < l2) {
|
||||
auto d2 = (pt - v1).squaredNorm() - sqr(t) / l2;
|
||||
auto d = sqrt(std::max(d2, 0.f));
|
||||
if (&pt == &v3) {
|
||||
if (d < EPSILON)
|
||||
printf("Degenerate triangle!\n");
|
||||
} else {
|
||||
assert(d > EPSILON);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // EXPENSIVE_DEBUG_CHECKS
|
||||
|
||||
std::vector<stl_vertex> vertices_transformed = transform_mesh_vertices_for_slicing(mesh, trafo);
|
||||
|
||||
std::vector<FaceOrientation> face_orientation(mesh.indices.size(), FaceOrientation::Up);
|
||||
for (const stl_triangle_vertex_indices &tri : mesh.indices) {
|
||||
const Vec3f fa = vertices_transformed[tri(0)];
|
||||
const Vec3f fb = vertices_transformed[tri(1)];
|
||||
const Vec3f fc = vertices_transformed[tri(2)];
|
||||
assert(fa != fb && fa != fc && fb != fc);
|
||||
const Point a = to_2d(fa).cast<coord_t>();
|
||||
const Point b = to_2d(fb).cast<coord_t>();
|
||||
const Point c = to_2d(fc).cast<coord_t>();
|
||||
const int64_t d = cross2((b - a).cast<int64_t>(), (c - b).cast<int64_t>());
|
||||
FaceOrientation fo = FaceOrientation::Vertical;
|
||||
if (d > 0)
|
||||
fo = FaceOrientation::Up;
|
||||
else if (d < 0)
|
||||
fo = FaceOrientation::Down;
|
||||
else {
|
||||
// Is the triangle vertical or degenerate?
|
||||
assert(d == 0);
|
||||
fo = fa == fb || fa == fc || fb == fc ? FaceOrientation::Degenerate : FaceOrientation::Vertical;
|
||||
}
|
||||
face_orientation[&tri - mesh.indices.data()] = fo;
|
||||
}
|
||||
|
||||
std::vector<Vec3i> face_neighbors = its_face_neighbors_par(mesh);
|
||||
int num_edges;
|
||||
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_neighbors, true, &num_edges);
|
||||
std::pair<SlabLines, SlabLines> lines = slice_slabs_make_lines(
|
||||
vertices_transformed, mesh.indices, face_neighbors, face_edge_ids, num_edges, face_orientation, zs,
|
||||
out_top != nullptr, out_bottom != nullptr, throw_on_cancel);
|
||||
|
||||
throw_on_cancel();
|
||||
|
||||
if (out_top)
|
||||
*out_top = make_slab_loops<true>(lines.first, num_edges, throw_on_cancel);
|
||||
if (out_bottom)
|
||||
*out_bottom = make_slab_loops<false>(lines.second, num_edges, throw_on_cancel);
|
||||
}
|
||||
|
||||
// Remove duplicates of slice_vertices, optionally triangulate the cut.
|
||||
static void triangulate_slice(
|
||||
indexed_triangle_set &its,
|
||||
@ -1308,7 +1977,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
||||
// To triangulate the caps after slicing.
|
||||
IntersectionLines upper_lines, lower_lines;
|
||||
std::vector<int> upper_slice_vertices, lower_slice_vertices;
|
||||
std::vector<Vec3i> facets_edges = create_face_neighbors_index(mesh);
|
||||
std::vector<Vec3i> facets_edge_ids = its_face_edge_ids(mesh);
|
||||
|
||||
for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) {
|
||||
const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx];
|
||||
@ -1329,7 +1998,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
||||
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);
|
||||
slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
|
||||
}
|
||||
|
||||
if (slice_type != FacetSliceType::NoSlice) {
|
||||
@ -1371,8 +2040,8 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
||||
// 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) {
|
||||
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id ||facets_edge_ids[facet_idx](iv) == line.edge_b_id);
|
||||
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
|
||||
v0v1 = to_3d(unscaled<float>(line.a), z);
|
||||
v2v0 = to_3d(unscaled<float>(line.b), z);
|
||||
} else {
|
||||
|
@ -77,6 +77,21 @@ inline std::vector<ExPolygons> slice_mesh_ex(
|
||||
return slice_mesh_ex(mesh, zs, params, throw_on_cancel);
|
||||
}
|
||||
|
||||
// Slice a triangle set with a set of Z slabs (thick layers).
|
||||
// The effect is similar to producing the usual top / bottom layers from a sliced mesh by
|
||||
// subtracting layer[i] from layer[i - 1] for the top surfaces resp.
|
||||
// subtracting layer[i] from layer[i + 1] for the bottom surfaces,
|
||||
// with the exception that the triangle set this function processes may not cover the whole top resp. bottom surface.
|
||||
// top resp. bottom surfaces are calculated only if out_top resp. out_bottom is not null.
|
||||
void slice_mesh_slabs(
|
||||
const indexed_triangle_set &mesh,
|
||||
// Unscaled Zs
|
||||
const std::vector<float> &zs,
|
||||
const Transform3d &trafo,
|
||||
std::vector<Polygons> *out_top,
|
||||
std::vector<Polygons> *out_bottom,
|
||||
std::function<void()> throw_on_cancel);
|
||||
|
||||
void cut_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
float z,
|
||||
|
@ -810,7 +810,11 @@ int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, co
|
||||
void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state)
|
||||
{
|
||||
// Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
|
||||
m_triangles.reserve(m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1);
|
||||
{
|
||||
size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1;
|
||||
if (m_triangles.capacity() < num_triangles_new)
|
||||
m_triangles.reserve(next_highest_power_of_2(num_triangles_new));
|
||||
}
|
||||
|
||||
Triangle &tr = m_triangles[facet_idx];
|
||||
assert(tr.is_split());
|
||||
@ -918,7 +922,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta
|
||||
++ num_vertices;
|
||||
out.vertices.reserve(num_vertices);
|
||||
std::vector<int> vertex_map(m_vertices.size(), -1);
|
||||
for (int i = 0; i < m_vertices.size(); ++ i)
|
||||
for (size_t i = 0; i < m_vertices.size(); ++ i)
|
||||
if (const Vertex &v = m_vertices[i]; v.ref_cnt > 0) {
|
||||
vertex_map[i] = int(out.vertices.size());
|
||||
out.vertices.emplace_back(v.v);
|
||||
@ -958,10 +962,13 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
|
||||
this->triangle_midpoint(neighbors(1), vertices(2), vertices(1)),
|
||||
this->triangle_midpoint(neighbors(2), vertices(0), vertices(2)));
|
||||
int splits = (midpoints(0) != -1) + (midpoints(1) != -1) + (midpoints(2) != -1);
|
||||
if (splits == 0) {
|
||||
switch (splits) {
|
||||
case 0:
|
||||
// Just emit this triangle.
|
||||
out_triangles.emplace_back(vertices(0), midpoints(0), midpoints(2));
|
||||
} else if (splits == 1) {
|
||||
out_triangles.emplace_back(vertices(0), vertices(1), vertices(2));
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
// Split to two triangles
|
||||
int i = midpoints(0) != -1 ? 2 : midpoints(1) != -1 ? 0 : 1;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
@ -969,16 +976,19 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
|
||||
this->get_facets_split_by_tjoints(
|
||||
{ vertices(i), vertices(j), midpoints(j) },
|
||||
{ neighbors(i),
|
||||
this->neighbor_child(neighbors(j), vertices(j), vertices(k), Partition::Second),
|
||||
this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::Second),
|
||||
-1 },
|
||||
out_triangles);
|
||||
this->get_facets_split_by_tjoints(
|
||||
{ midpoints(j), vertices(j), vertices(k) },
|
||||
{ this->neighbor_child(neighbors(j), vertices(j), vertices(k), Partition::First),
|
||||
{ midpoints(j), vertices(k), vertices(i) },
|
||||
{ this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::First),
|
||||
neighbors(k),
|
||||
-1 },
|
||||
out_triangles);
|
||||
} else if (splits == 2) {
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// Split to three triangles.
|
||||
int i = midpoints(0) == -1 ? 2 : midpoints(1) == -1 ? 0 : 1;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
@ -1000,7 +1010,10 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
|
||||
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::Second),
|
||||
-1 },
|
||||
out_triangles);
|
||||
} else if (splits == 4) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(splits == 3);
|
||||
// Split to 4 triangles.
|
||||
this->get_facets_split_by_tjoints(
|
||||
{ vertices(0), midpoints(0), midpoints(2) },
|
||||
@ -1021,6 +1034,7 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
|
||||
-1 },
|
||||
out_triangles);
|
||||
out_triangles.emplace_back(midpoints);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1106,6 +1120,13 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
||||
{
|
||||
reset(); // dump any current state
|
||||
|
||||
// Reserve number of triangles as if each triangle was saved with 4 bits.
|
||||
// With MMU painting this estimate may be somehow low, but better than nothing.
|
||||
m_triangles.reserve(std::max(m_mesh->its.indices.size(), data.second.size() / 4));
|
||||
// Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
|
||||
// Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
|
||||
m_vertices.reserve(std::max(m_mesh->its.vertices.size(), m_triangles.size() / 2));
|
||||
|
||||
// Vector to store all parents that have offsprings.
|
||||
struct ProcessingInfo {
|
||||
int facet_id = 0;
|
||||
@ -1203,7 +1224,8 @@ bool TriangleSelector::has_facets(const std::pair<std::vector<std::pair<int, int
|
||||
std::vector<int> parents_children;
|
||||
parents_children.reserve(64);
|
||||
|
||||
for (auto [triangle_id, ibit] : data.first) {
|
||||
for (const std::pair<int, int> &triangle_id_and_ibit : data.first) {
|
||||
int ibit = triangle_id_and_ibit.second;
|
||||
assert(ibit < data.second.size());
|
||||
auto next_nibble = [&data, &ibit = ibit]() {
|
||||
int n = 0;
|
||||
|
@ -39,26 +39,22 @@ enum ModelInstanceEPrintVolumeState : unsigned char;
|
||||
// possibly indexed by triangles and / or quads.
|
||||
class GLIndexedVertexArray {
|
||||
public:
|
||||
GLIndexedVertexArray() :
|
||||
vertices_and_normals_interleaved_VBO_id(0),
|
||||
triangle_indices_VBO_id(0),
|
||||
quad_indices_VBO_id(0)
|
||||
{}
|
||||
// Only Eigen types of Nx16 size are vectorized. This bounding box will not be vectorized.
|
||||
static_assert(sizeof(Eigen::AlignedBox<float, 3>) == 24, "Eigen::AlignedBox<float, 3> is not being vectorized, thus it does not need to be aligned");
|
||||
using BoundingBox = Eigen::AlignedBox<float, 3>;
|
||||
|
||||
GLIndexedVertexArray() { m_bounding_box.setEmpty(); }
|
||||
GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
|
||||
vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
|
||||
triangle_indices(rhs.triangle_indices),
|
||||
quad_indices(rhs.quad_indices),
|
||||
vertices_and_normals_interleaved_VBO_id(0),
|
||||
triangle_indices_VBO_id(0),
|
||||
quad_indices_VBO_id(0)
|
||||
{ assert(! rhs.has_VBOs()); }
|
||||
m_bounding_box(rhs.m_bounding_box)
|
||||
{ assert(! rhs.has_VBOs()); m_bounding_box.setEmpty(); }
|
||||
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
|
||||
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
|
||||
triangle_indices(std::move(rhs.triangle_indices)),
|
||||
quad_indices(std::move(rhs.quad_indices)),
|
||||
vertices_and_normals_interleaved_VBO_id(0),
|
||||
triangle_indices_VBO_id(0),
|
||||
quad_indices_VBO_id(0)
|
||||
m_bounding_box(rhs.m_bounding_box)
|
||||
{ assert(! rhs.has_VBOs()); }
|
||||
|
||||
~GLIndexedVertexArray() { release_geometry(); }
|
||||
@ -92,7 +88,7 @@ public:
|
||||
this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved);
|
||||
this->triangle_indices = std::move(rhs.triangle_indices);
|
||||
this->quad_indices = std::move(rhs.quad_indices);
|
||||
this->m_bounding_box = std::move(rhs.m_bounding_box);
|
||||
this->m_bounding_box = rhs.m_bounding_box;
|
||||
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
|
||||
this->triangle_indices_size = rhs.triangle_indices_size;
|
||||
this->quad_indices_size = rhs.quad_indices_size;
|
||||
@ -147,7 +143,7 @@ public:
|
||||
this->vertices_and_normals_interleaved.emplace_back(z);
|
||||
|
||||
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
|
||||
m_bounding_box.merge(Vec3f(x, y, z).cast<double>());
|
||||
m_bounding_box.extend(Vec3f(x, y, z));
|
||||
};
|
||||
|
||||
inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
|
||||
@ -203,10 +199,10 @@ public:
|
||||
this->vertices_and_normals_interleaved.clear();
|
||||
this->triangle_indices.clear();
|
||||
this->quad_indices.clear();
|
||||
this->m_bounding_box.reset();
|
||||
vertices_and_normals_interleaved_size = 0;
|
||||
triangle_indices_size = 0;
|
||||
quad_indices_size = 0;
|
||||
m_bounding_box.setEmpty();
|
||||
}
|
||||
|
||||
// Shrink the internal storage to tighly fit the data stored.
|
||||
@ -216,7 +212,7 @@ public:
|
||||
this->quad_indices.shrink_to_fit();
|
||||
}
|
||||
|
||||
const BoundingBoxf3& bounding_box() const { return m_bounding_box; }
|
||||
const BoundingBox& bounding_box() const { return m_bounding_box; }
|
||||
|
||||
// Return an estimate of the memory consumed by this class.
|
||||
size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
|
||||
@ -235,7 +231,7 @@ public:
|
||||
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
|
||||
|
||||
private:
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
BoundingBox m_bounding_box;
|
||||
};
|
||||
|
||||
class GLVolume {
|
||||
@ -355,7 +351,15 @@ public:
|
||||
std::vector<size_t> offsets;
|
||||
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
const BoundingBoxf3& bounding_box() const { return this->indexed_vertex_array.bounding_box(); }
|
||||
BoundingBoxf3 bounding_box() const {
|
||||
BoundingBoxf3 out;
|
||||
if (! this->indexed_vertex_array.bounding_box().isEmpty()) {
|
||||
out.min = this->indexed_vertex_array.bounding_box().min().cast<double>();
|
||||
out.max = this->indexed_vertex_array.bounding_box().max().cast<double>();
|
||||
out.defined = true;
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
void set_render_color(float r, float g, float b, float a);
|
||||
void set_render_color(const float* rgba, unsigned int size);
|
||||
|
@ -470,52 +470,50 @@ void TriangleSelectorMmuGui::render(ImGuiWrapper *imgui)
|
||||
{
|
||||
static constexpr std::array<float, 4> seed_fill_color{0.f, 1.f, 0.44f, 1.f};
|
||||
|
||||
std::vector<int> color_cnt(m_iva_colors.size());
|
||||
int seed_fill_cnt = 0;
|
||||
for (auto &iva_color : m_iva_colors)
|
||||
iva_color.release_geometry();
|
||||
m_iva_seed_fill.release_geometry();
|
||||
|
||||
auto append_triangle = [this](GLIndexedVertexArray &iva, int &cnt, const Triangle &tr) -> void {
|
||||
for (int i = 0; i < 3; ++i)
|
||||
iva.push_geometry(m_vertices[tr.verts_idxs[i]].v, m_mesh->stl.facet_start[tr.source_triangle].normal);
|
||||
iva.push_triangle(cnt, cnt + 1, cnt + 2);
|
||||
cnt += 3;
|
||||
};
|
||||
|
||||
for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) {
|
||||
for (const Triangle &tr : m_triangles) {
|
||||
if (!tr.valid() || tr.is_split() || tr.is_selected_by_seed_fill() || tr.get_state() != EnforcerBlockerType(color_idx))
|
||||
continue;
|
||||
append_triangle(m_iva_colors[color_idx], color_cnt[color_idx], tr);
|
||||
for (const Triangle &tr : m_triangles)
|
||||
if (tr.valid() && ! tr.is_split()) {
|
||||
GLIndexedVertexArray *iva = nullptr;
|
||||
if (tr.is_selected_by_seed_fill())
|
||||
iva = &m_iva_seed_fill;
|
||||
else if (int color = int(tr.get_state()); color < int(m_iva_colors.size()))
|
||||
iva = &m_iva_colors[color];
|
||||
if (iva) {
|
||||
if (iva->vertices_and_normals_interleaved.size() + 18 > iva->vertices_and_normals_interleaved.capacity())
|
||||
iva->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(iva->vertices_and_normals_interleaved.size() + 18));
|
||||
const Vec3f &n = m_mesh->stl.facet_start[tr.source_triangle].normal;
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
const Vec3f &v = m_vertices[tr.verts_idxs[i]].v;
|
||||
iva->vertices_and_normals_interleaved.emplace_back(n.x());
|
||||
iva->vertices_and_normals_interleaved.emplace_back(n.y());
|
||||
iva->vertices_and_normals_interleaved.emplace_back(n.z());
|
||||
iva->vertices_and_normals_interleaved.emplace_back(v.x());
|
||||
iva->vertices_and_normals_interleaved.emplace_back(v.y());
|
||||
iva->vertices_and_normals_interleaved.emplace_back(v.z());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const Triangle &tr : m_triangles) {
|
||||
if (!tr.valid() || tr.is_split() || !tr.is_selected_by_seed_fill())
|
||||
continue;
|
||||
append_triangle(m_iva_seed_fill, seed_fill_cnt, tr);
|
||||
}
|
||||
|
||||
for (auto &iva_color : m_iva_colors)
|
||||
iva_color.finalize_geometry(true);
|
||||
m_iva_seed_fill.finalize_geometry(true);
|
||||
|
||||
auto *shader = wxGetApp().get_current_shader();
|
||||
auto* shader = wxGetApp().get_current_shader();
|
||||
if (!shader)
|
||||
return;
|
||||
assert(shader->get_name() == "gouraud");
|
||||
|
||||
auto render = [&shader](const GLIndexedVertexArray &iva, const std::array<float, 4> &color) -> void {
|
||||
if (iva.has_VBOs()) {
|
||||
shader->set_uniform("uniform_color", color);
|
||||
for (size_t i = 0; i <= m_iva_colors.size(); ++i)
|
||||
if (GLIndexedVertexArray &iva = i == m_iva_colors.size() ? m_iva_seed_fill : m_iva_colors[i];
|
||||
! iva.vertices_and_normals_interleaved.empty()) {
|
||||
iva.vertices_and_normals_interleaved_size = iva.vertices_and_normals_interleaved.size();
|
||||
iva.triangle_indices.assign(iva.vertices_and_normals_interleaved_size / 6, 0);
|
||||
std::iota(iva.triangle_indices.begin(), iva.triangle_indices.end(), 0);
|
||||
iva.triangle_indices_size = iva.triangle_indices.size();
|
||||
iva.finalize_geometry(true);
|
||||
shader->set_uniform("uniform_color",
|
||||
(i == 0) ? m_default_volume_color : i == m_iva_colors.size() ? seed_fill_color : m_colors[i - 1]);
|
||||
iva.render();
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx)
|
||||
render(m_iva_colors[color_idx], (color_idx == 0) ? m_default_volume_color : m_colors[color_idx - 1]);
|
||||
render(m_iva_seed_fill, seed_fill_color);
|
||||
}
|
||||
|
||||
wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
|
||||
|
@ -685,7 +685,7 @@ void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui)
|
||||
va = &m_varrays[ORIGINAL];
|
||||
cnt = &cnts[ORIGINAL];
|
||||
}
|
||||
else if (tr.valid) {
|
||||
else if (tr.valid()) {
|
||||
va = &m_varrays[SPLIT];
|
||||
cnt = &cnts[SPLIT];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user