Refactoring - running the support placement in gui worker

debug UI
debug subdivide
This commit is contained in:
PavelMikus 2022-03-23 13:10:20 +01:00
parent d265d781d4
commit 2b9741da68
11 changed files with 950 additions and 467 deletions

View File

@ -50,6 +50,8 @@ set(SLIC3R_SOURCES
ExtrusionEntityCollection.hpp ExtrusionEntityCollection.hpp
ExtrusionSimulator.cpp ExtrusionSimulator.cpp
ExtrusionSimulator.hpp ExtrusionSimulator.hpp
FDMSupportSpots.cpp
FDMSupportSpots.hpp
FileParserError.hpp FileParserError.hpp
Fill/Fill.cpp Fill/Fill.cpp
Fill/Fill.hpp Fill/Fill.hpp
@ -225,6 +227,8 @@ set(SLIC3R_SOURCES
SlicesToTriangleMesh.cpp SlicesToTriangleMesh.cpp
SlicingAdaptive.cpp SlicingAdaptive.cpp
SlicingAdaptive.hpp SlicingAdaptive.hpp
Subdivide.cpp
Subdivide.hpp
SupportMaterial.cpp SupportMaterial.cpp
SupportMaterial.hpp SupportMaterial.hpp
Surface.cpp Surface.cpp

View File

@ -0,0 +1,297 @@
#include "FDMSupportSpots.hpp"
namespace Slic3r {
inline Vec3f value_to_rgbf(float minimum, float maximum, float value) {
float ratio = 2.0f * (value - minimum) / (maximum - minimum);
float b = std::max(0.0f, (1.0f - ratio));
float r = std::max(0.0f, (ratio - 1.0f));
float g = 1.0f - b - r;
return Vec3f { r, g, b };
}
inline float face_area(const stl_vertex vertex[3]) {
return (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).norm() / 2;
}
inline float its_face_area(const indexed_triangle_set &its, const stl_triangle_vertex_indices face) {
const stl_vertex vertices[3] { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
return face_area(vertices);
}
inline float its_face_area(const indexed_triangle_set &its, const int face_idx) {
return its_face_area(its, its.indices[face_idx]);
}
FDMSupportSpots::FDMSupportSpots(FDMSupportSpotsConfig config, indexed_triangle_set mesh, const Transform3d &transform) :
m_config(config) {
using namespace FDMSupportSpotsImpl;
// Transform and subdivide first
its_transform(mesh, transform);
// m_mesh = its_subdivide(mesh, config.max_side_length);
m_mesh = mesh;
// Prepare data structures
auto neighbours = its_face_neighbors_par(mesh);
for (size_t face_index = 0; face_index < mesh.indices.size(); ++face_index) {
Vec3f normal = its_face_normal(mesh, face_index);
//extract vertices
std::vector<Vec3f> vertices { mesh.vertices[mesh.indices[face_index][0]],
mesh.vertices[mesh.indices[face_index][1]], mesh.vertices[mesh.indices[face_index][2]] };
//sort vertices by z
std::sort(vertices.begin(), vertices.end(), [](const Vec3f &a, const Vec3f &b) {
return a.z() < b.z();
});
Vec3f lowest_edge_a = (vertices[1] - vertices[0]).normalized();
Vec3f lowest_edge_b = (vertices[2] - vertices[0]).normalized();
Vec3f down = -Vec3f::UnitZ();
Triangle t { };
t.indices = mesh.indices[face_index];
t.normal = normal;
t.downward_dot_value = normal.dot(down);
t.index = face_index;
t.neighbours = neighbours[face_index];
t.lowest_z_coord = vertices[0].z();
t.edge_dot_value = std::max(lowest_edge_a.dot(down), lowest_edge_b.dot(down));
t.area = its_face_area(mesh, face_index);
this->m_triangles.push_back(t);
this->m_triangle_indexes_by_z.push_back(face_index);
}
assert(this->m_triangle_indexes_by_z.size() == this->m_triangles.size());
assert(mesh.indices.size() == this->m_triangles.size());
std::sort(this->m_triangle_indexes_by_z.begin(), this->m_triangle_indexes_by_z.end(),
[&](const size_t &left, const size_t &right) {
if (this->m_triangles[left].lowest_z_coord != this->m_triangles[right].lowest_z_coord) {
return this->m_triangles[left].lowest_z_coord < this->m_triangles[right].lowest_z_coord;
} else {
return this->m_triangles[left].edge_dot_value > this->m_triangles[right].edge_dot_value;
}
});
for (size_t order_index = 0; order_index < this->m_triangle_indexes_by_z.size(); ++order_index) {
this->m_triangles[this->m_triangle_indexes_by_z[order_index]].order_by_z = order_index;
}
for (Triangle &triangle : this->m_triangles) {
std::sort(begin(triangle.neighbours), end(triangle.neighbours), [&](const size_t left, const size_t right) {
if (left < 0 || right < 0) {
return true;
}
return this->m_triangles[left].order_by_z < this->m_triangles[right].order_by_z;
});
}
}
float FDMSupportSpots::triangle_vertices_shortest_distance(const indexed_triangle_set &its, const size_t &face_a,
const size_t &face_b) const {
float distance = std::numeric_limits<float>::max();
for (const auto &vertex_a_index : its.indices[face_a]) {
for (const auto &vertex_b_index : its.indices[face_b]) {
distance = std::min(distance, (its.vertices[vertex_a_index] - its.vertices[vertex_b_index]).norm());
}
}
return distance;
}
void FDMSupportSpots::find_support_areas() {
using namespace FDMSupportSpotsImpl;
size_t next_group_id = 1;
for (const size_t &current_index : this->m_triangle_indexes_by_z) {
Triangle &current = this->m_triangles[current_index];
size_t group_id = 0;
float neighbourhood_unsupported_area = 0;
bool visited_neighbour = false;
std::queue<int> neighbours { };
std::set<int> explored { };
for (const auto &direct_neighbour_index : current.neighbours) {
if (direct_neighbour_index < 0 || !this->m_triangles[direct_neighbour_index].visited) {
continue;
}
neighbours.push(direct_neighbour_index);
const Triangle &direct_neighbour = this->m_triangles[direct_neighbour_index];
if (neighbourhood_unsupported_area <= direct_neighbour.unsupported_weight) {
neighbourhood_unsupported_area = direct_neighbour.unsupported_weight;
group_id = direct_neighbour.group_id;
}
visited_neighbour = true;
}
while (!neighbours.empty() && !visited_neighbour) {
int neighbour_index = neighbours.front();
neighbours.pop();
explored.insert(neighbour_index);
const Triangle &neighbour = this->m_triangles[neighbour_index];
if (explored.find(neighbour_index) != explored.end()
|| triangle_vertices_shortest_distance(this->m_mesh, current.index, neighbour_index)
> this->m_config.islands_tolerance_distance) {
// not visited, already explored, or too far
continue;
}
if (neighbour.visited) {
visited_neighbour = true;
break;
}
for (const auto &neighbour_index : neighbour.neighbours) {
if (neighbour_index < 0) {
continue;
}
neighbours.push(neighbour_index);
}
}
current.visited = true;
current.unsupported_weight =
current.downward_dot_value >= this->m_config.limit_angle_cos ?
current.area + neighbourhood_unsupported_area : 0;
current.group_id = group_id;
if (current.downward_dot_value > 0
&& (current.unsupported_weight > this->m_config.patch_spacing || !visited_neighbour)) {
group_id = next_group_id;
next_group_id++;
std::queue<int> supporters { };
current.visited = false;
supporters.push(int(current_index));
float supported_size = 0;
while (supported_size < this->m_config.patch_size && !supporters.empty()) {
int s = supporters.front();
supporters.pop();
Triangle &supporter = this->m_triangles[s];
if (supporter.downward_dot_value <= 0.1) {
continue;
}
if (supporter.visited) {
supported_size += supporter.supports ? supporter.area : 0;
} else {
supporter.supports = true;
supporter.unsupported_weight = 0;
supported_size += supporter.area;
supporter.visited = true;
supporter.group_id = group_id;
for (const auto &n : supporter.neighbours) {
if (n < 0)
continue;
supporters.push(n);
}
}
}
}
}
}
#ifdef DEBUG_FILES
void FDMSupportSpots::debug_export() const {
using namespace FDMSupportSpotsImpl;
Slic3r::CNumericLocalesSetter locales_setter;
{
std::string file_name = debug_out_path("groups.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < this->m_triangles.size(); ++i) {
Vec3f color = value_to_rgbf(0.0f, 19.0f, float(this->m_triangles[i].group_id % 20));
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", this->m_mesh.vertices[this->m_triangles[i].indices[index]](0),
this->m_mesh.vertices[this->m_triangles[i].indices[index]](1),
this->m_mesh.vertices[this->m_triangles[i].indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
{
std::string file_name = debug_out_path("sorted.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < this->m_triangle_indexes_by_z.size(); ++i) {
const Triangle &triangle = this->m_triangles[this->m_triangle_indexes_by_z[i]];
Vec3f color = Vec3f { float(i) / float(this->m_triangle_indexes_by_z.size()), float(i)
/ float(this->m_triangle_indexes_by_z.size()), 0.5 };
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", this->m_mesh.vertices[triangle.indices[index]](0),
this->m_mesh.vertices[triangle.indices[index]](1),
this->m_mesh.vertices[triangle.indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
{
std::string file_name = debug_out_path("weight.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < this->m_triangle_indexes_by_z.size(); ++i) {
const Triangle &triangle = this->m_triangles[this->m_triangle_indexes_by_z[i]];
Vec3f color = value_to_rgbf(0, 10, triangle.area);
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", this->m_mesh.vertices[triangle.indices[index]](0),
this->m_mesh.vertices[triangle.indices[index]](1),
this->m_mesh.vertices[triangle.indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
{
std::string file_name = debug_out_path("dot_value.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < this->m_triangle_indexes_by_z.size(); ++i) {
const Triangle &triangle = this->m_triangles[this->m_triangle_indexes_by_z[i]];
Vec3f color = value_to_rgbf(-1, 1, triangle.downward_dot_value);
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", this->m_mesh.vertices[triangle.indices[index]](0),
this->m_mesh.vertices[triangle.indices[index]](1),
this->m_mesh.vertices[triangle.indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
}
#endif
}

View File

@ -0,0 +1,73 @@
#ifndef SRC_LIBSLIC3R_FDMSUPPORTSPOTS_HPP_
#define SRC_LIBSLIC3R_FDMSUPPORTSPOTS_HPP_
#include "libslic3r/Model.hpp"
#include <algorithm>
#include <queue>
#include <boost/nowide/cstdio.hpp>
#include <boost/log/trivial.hpp>
#define DEBUG_FILES
#ifdef DEBUG_FILES
#include <boost/nowide/cstdio.hpp>
#include "libslic3r/Utils.hpp"
#endif
namespace Slic3r {
namespace FDMSupportSpotsImpl {
struct Triangle {
stl_triangle_vertex_indices indices;
Vec3f normal;
float downward_dot_value;
size_t index;
Vec3i neighbours;
float lowest_z_coord;
// higher value of dot product of the downward direction and the two bottom edges
float edge_dot_value;
float area;
size_t order_by_z;
//members updated during algorithm
float unsupported_weight { 0.0 };
bool supports = false;
bool visited = false;
size_t group_id = 0;
};
}
struct FDMSupportSpotsConfig {
float limit_angle_cos { 35.0f * PI / 180.0f };
float patch_size { 6.0f };
float patch_spacing { 6.0f };
float islands_tolerance_distance { 1.0f };
float max_side_length { 1.0f };
};
struct FDMSupportSpots {
FDMSupportSpotsConfig m_config;
indexed_triangle_set m_mesh;
std::vector<FDMSupportSpotsImpl::Triangle> m_triangles;
std::vector<size_t> m_triangle_indexes_by_z;
explicit FDMSupportSpots(FDMSupportSpotsConfig config, indexed_triangle_set mesh, const Transform3d& transform);
float triangle_vertices_shortest_distance(const indexed_triangle_set &its, const size_t &face_a,
const size_t &face_b) const;
void find_support_areas();
#ifdef DEBUG_FILES
void debug_export() const;
#endif
}
;
}
#endif /* SRC_LIBSLIC3R_FDMSUPPORTSPOTS_HPP_ */

218
src/libslic3r/Subdivide.cpp Normal file
View File

@ -0,0 +1,218 @@
#include "Subdivide.hpp"
#include "Point.hpp"
namespace Slic3r{
indexed_triangle_set its_subdivide(
const indexed_triangle_set &its, float max_length)
{
// same order as key order in Edge Divides
struct VerticesSequence
{
size_t start_index;
bool positive_order;
VerticesSequence(size_t start_index, bool positive_order = true)
: start_index(start_index), positive_order(positive_order){}
};
// vertex index small, big vertex index from key.first to key.second
using EdgeDivides = std::map<std::pair<size_t, size_t>, VerticesSequence>;
struct Edges
{
Vec3f data[3];
Vec3f lengths;
Edges(const Vec3crd &indices, const std::vector<Vec3f> &vertices)
: lengths(-1.f,-1.f,-1.f)
{
const Vec3f &v0 = vertices[indices[0]];
const Vec3f &v1 = vertices[indices[1]];
const Vec3f &v2 = vertices[indices[2]];
data[0] = v0 - v1;
data[1] = v1 - v2;
data[2] = v2 - v0;
}
float abs_sum(const Vec3f &v)
{
return abs(v[0]) + abs(v[1]) + abs(v[2]);
}
bool is_dividable(const float& max_length) {
Vec3f sum(abs_sum(data[0]), abs_sum(data[1]), abs_sum(data[2]));
Vec3i biggest_index = (sum[0] > sum[1]) ?
((sum[0] > sum[2]) ?
((sum[2] > sum[1]) ?
Vec3i(0, 2, 1) :
Vec3i(0, 1, 2)) :
Vec3i(2, 0, 1)) :
((sum[1] > sum[2]) ?
((sum[2] > sum[0]) ?
Vec3i(1, 2, 0) :
Vec3i(1, 0, 2)) :
Vec3i(2, 1, 0));
for (int i = 0; i < 3; i++) {
int index = biggest_index[i];
if (sum[index] <= max_length) return false;
lengths[index] = data[index].norm();
if (lengths[index] <= max_length) continue;
// calculate rest of lengths
for (int j = i + 1; j < 3; j++) {
index = biggest_index[j];
lengths[index] = data[index].norm();
}
return true;
}
return false;
}
};
struct TriangleLengths
{
Vec3crd indices;
Vec3f l; // lengths
TriangleLengths(const Vec3crd &indices, const Vec3f &lengths)
: indices(indices), l(lengths)
{}
int get_divide_index(float max_length) {
if (l[0] > l[1] && l[0] > l[2]) {
if (l[0] > max_length) return 0;
} else if (l[1] > l[2]) {
if (l[1] > max_length) return 1;
} else {
if (l[2] > max_length) return 2;
}
return -1;
}
// divide triangle add new vertex to vertices
std::pair<TriangleLengths, TriangleLengths> divide(
int divide_index, float max_length,
std::vector<Vec3f> &vertices,
EdgeDivides &edge_divides)
{
// index to lengths and indices
size_t i0 = divide_index;
size_t i1 = (divide_index + 1) % 3;
size_t vi0 = indices[i0];
size_t vi1 = indices[i1];
std::pair<size_t, size_t> key(vi0, vi1);
bool key_swap = false;
if (key.first > key.second) {
std::swap(key.first, key.second);
key_swap = true;
}
float length = l[divide_index];
size_t count_edge_vertices = static_cast<size_t>(floor(length / max_length));
float count_edge_segments = static_cast<float>(count_edge_vertices + 1);
auto it = edge_divides.find(key);
if (it == edge_divides.end()) {
// Create new vertices
VerticesSequence new_vs(vertices.size());
Vec3f vf = vertices[key.first]; // copy
const Vec3f &vs = vertices[key.second];
Vec3f dir = vs - vf;
for (size_t i = 1; i <= count_edge_vertices; ++i) {
float ratio = i / count_edge_segments;
vertices.push_back(vf + dir * ratio);
}
bool success;
std::tie(it,success) = edge_divides.insert({key, new_vs});
assert(success);
}
const VerticesSequence &vs = it->second;
int index_offset = count_edge_vertices/2;
size_t i2 = (divide_index + 2) % 3;
if (count_edge_vertices % 2 == 0 && key_swap == (l[i1] < l[i2])) {
--index_offset;
}
int sign = (vs.positive_order) ? 1 : -1;
size_t new_index = vs.start_index + sign*index_offset;
size_t vi2 = indices[i2];
const Vec3f &v2 = vertices[vi2];
Vec3f new_edge = v2 - vertices[new_index];
float new_len = new_edge.norm();
float ratio = (1 + index_offset) / count_edge_segments;
float len1 = l[i0] * ratio;
float len2 = l[i0] - len1;
if (key_swap) std::swap(len1, len2);
Vec3crd indices1(vi0, new_index, vi2);
Vec3f lengths1(len1, new_len, l[i2]);
Vec3crd indices2(new_index, vi1, vi2);
Vec3f lengths2(len2, l[i1], new_len);
// append key for divided edge when neccesary
if (index_offset > 0) {
std::pair<size_t, size_t> new_key(key.first, new_index);
bool new_key_swap = false;
if (new_key.first > new_key.second) {
std::swap(new_key.first, new_key.second);
new_key_swap = true;
}
if (edge_divides.find(new_key) == edge_divides.end()) {
// insert new
edge_divides.insert({new_key, (new_key_swap) ?
VerticesSequence(new_index - sign, !vs.positive_order)
: vs});
}
}
if (index_offset < int(count_edge_vertices)-1) {
std::pair<size_t, size_t> new_key(new_index, key.second);
bool new_key_swap = false;
if (new_key.first > new_key.second) {
std::swap(new_key.first, new_key.second);
new_key_swap = true;
}
// bad order
if (edge_divides.find(new_key) == edge_divides.end()) {
edge_divides.insert({new_key, (new_key_swap) ?
VerticesSequence(vs.start_index + sign*(count_edge_vertices-1), !vs.positive_order)
: VerticesSequence(new_index + sign, vs.positive_order)});
}
}
return {TriangleLengths(indices1, lengths1),
TriangleLengths(indices2, lengths2)};
}
};
indexed_triangle_set result;
result.indices.reserve(its.indices.size());
const std::vector<Vec3f> &vertices = its.vertices;
result.vertices = vertices; // copy
std::queue<TriangleLengths> tls;
EdgeDivides edge_divides;
for (const Vec3crd &indices : its.indices) {
Edges edges(indices, vertices);
// speed up only sum not sqrt is apply
if (!edges.is_dividable(max_length)) {
// small triangle
result.indices.push_back(indices);
continue;
}
TriangleLengths tl(indices, edges.lengths);
do {
int divide_index = tl.get_divide_index(max_length);
if (divide_index < 0) {
// no dividing
result.indices.push_back(tl.indices);
if (tls.empty()) break;
tl = tls.front(); // copy
tls.pop();
} else {
auto [tl1, tl2] = tl.divide(divide_index, max_length,
result.vertices, edge_divides);
tl = tl1;
tls.push(tl2);
}
} while (true);
}
return result;
}
}

View File

@ -0,0 +1,12 @@
#ifndef libslic3r_Subdivide_hpp_
#define libslic3r_Subdivide_hpp_
#include "TriangleMesh.hpp"
namespace Slic3r {
indexed_triangle_set its_subdivide(const indexed_triangle_set &its, float max_length);
}
#endif //libslic3r_Subdivide_hpp_

View File

@ -47,7 +47,6 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoSlaSupports.hpp GUI/Gizmos/GLGizmoSlaSupports.hpp
GUI/Gizmos/GLGizmoFdmSupports.cpp GUI/Gizmos/GLGizmoFdmSupports.cpp
GUI/Gizmos/GLGizmoFdmSupports.hpp GUI/Gizmos/GLGizmoFdmSupports.hpp
GUI/Gizmos/Experiment.cpp
GUI/Gizmos/GLGizmoFlatten.cpp GUI/Gizmos/GLGizmoFlatten.cpp
GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoFlatten.hpp
GUI/Gizmos/GLGizmoCut.cpp GUI/Gizmos/GLGizmoCut.cpp
@ -179,6 +178,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Jobs/ArrangeJob.cpp GUI/Jobs/ArrangeJob.cpp
GUI/Jobs/RotoptimizeJob.hpp GUI/Jobs/RotoptimizeJob.hpp
GUI/Jobs/RotoptimizeJob.cpp GUI/Jobs/RotoptimizeJob.cpp
GUI/Jobs/FDMSupportSpotsJob.hpp
GUI/Jobs/FDMSupportSpotsJob.cpp
GUI/Jobs/FillBedJob.hpp GUI/Jobs/FillBedJob.hpp
GUI/Jobs/FillBedJob.cpp GUI/Jobs/FillBedJob.cpp
GUI/Jobs/SLAImportJob.hpp GUI/Jobs/SLAImportJob.hpp

View File

@ -1,353 +0,0 @@
#include "libslic3r/Model.hpp"
#include <algorithm>
#include <queue>
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
#include "libslic3r/TriangleSelector.hpp"
#include <boost/nowide/cstdio.hpp>
#include <boost/log/trivial.hpp>
#define DEBUG_FILES
#ifdef DEBUG_FILES
#include <boost/nowide/cstdio.hpp>
#include "libslic3r/Utils.hpp"
#endif
namespace Slic3r::GUI {
//TODO close in seprate namespace
struct Triangle {
stl_triangle_vertex_indices indices;
Vec3f normal;
float downward_dot_value;
size_t index;
Vec3i neighbours;
float lowest_z_coord;
// higher value of dot product of the downward direction and the two bottom edges
float edge_dot_value;
float area;
size_t order_by_z;
//members updated during algorithm
float unsupported_weight { 0.0 };
bool supports = false;
bool visited = false;
size_t group_id = 0;
};
inline Vec3f value_to_rgbf(float minimum, float maximum, float value) {
float ratio = 2.0f * (value - minimum) / (maximum - minimum);
float b = std::max(0.0f, (1.0f - ratio));
float r = std::max(0.0f, (ratio - 1.0f));
float g = 1.0f - b - r;
return Vec3f { r, g, b };
}
inline float face_area(const stl_vertex vertex[3]) {
return (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).norm() / 2;
}
inline float its_face_area(const indexed_triangle_set &its, const stl_triangle_vertex_indices face) {
const stl_vertex vertices[3] { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
return face_area(vertices);
}
inline float its_face_area(const indexed_triangle_set &its, const int face_idx) {
return its_face_area(its, its.indices[face_idx]);
}
struct SupportPlacerMesh {
const float unsupported_area_limit;
const float patch_size;
const float limit_angle_cos;
const float neighbour_max_distance = 2.0;
indexed_triangle_set mesh;
std::vector<Triangle> triangles;
std::vector<size_t> triangle_indexes_by_z;
explicit SupportPlacerMesh(indexed_triangle_set &&t_mesh, float dot_limit, float patch_size,
float unsupported_area_limit) :
mesh(t_mesh), limit_angle_cos(dot_limit), patch_size(patch_size), unsupported_area_limit(
unsupported_area_limit) {
auto neighbours = its_face_neighbors_par(mesh);
for (size_t face_index = 0; face_index < mesh.indices.size(); ++face_index) {
Vec3f normal = its_face_normal(mesh, face_index);
//extract vertices
std::vector<Vec3f> vertices { mesh.vertices[mesh.indices[face_index][0]],
mesh.vertices[mesh.indices[face_index][1]], mesh.vertices[mesh.indices[face_index][2]] };
//sort vertices by z
std::sort(vertices.begin(), vertices.end(), [](const Vec3f &a, const Vec3f &b) {
return a.z() < b.z();
});
Vec3f lowest_edge_a = (vertices[1] - vertices[0]).normalized();
Vec3f lowest_edge_b = (vertices[2] - vertices[0]).normalized();
Vec3f down = -Vec3f::UnitZ();
Triangle t { };
t.indices = mesh.indices[face_index];
t.normal = normal;
t.downward_dot_value = normal.dot(down);
t.index = face_index;
t.neighbours = neighbours[face_index];
t.lowest_z_coord = vertices[0].z();
t.edge_dot_value = std::max(lowest_edge_a.dot(down), lowest_edge_b.dot(down));
t.area = its_face_area(mesh, face_index);
triangles.push_back(t);
triangle_indexes_by_z.push_back(face_index);
}
assert(triangle_indexes_by_z.size() == triangles.size());
assert(mesh.indices.size() == triangles.size());
std::sort(triangle_indexes_by_z.begin(), triangle_indexes_by_z.end(),
[&](const size_t &left, const size_t &right) {
if (triangles[left].lowest_z_coord != triangles[right].lowest_z_coord) {
return triangles[left].lowest_z_coord < triangles[right].lowest_z_coord;
} else {
return triangles[left].edge_dot_value > triangles[right].edge_dot_value;
}
});
for (size_t order_index = 0; order_index < triangle_indexes_by_z.size(); ++order_index) {
triangles[triangle_indexes_by_z[order_index]].order_by_z = order_index;
}
for (Triangle &triangle : triangles) {
std::sort(begin(triangle.neighbours), end(triangle.neighbours), [&](const size_t left, const size_t right) {
if (left < 0 || right < 0) {
return true;
}
return triangles[left].order_by_z < triangles[right].order_by_z;
});
}
}
float triangle_vertices_shortest_distance(const indexed_triangle_set &its, const size_t &face_a,
const size_t &face_b) {
float distance = std::numeric_limits<float>::max();
for (const auto &vertex_a_index : its.indices[face_a]) {
for (const auto &vertex_b_index : its.indices[face_b]) {
distance = std::min(distance, (its.vertices[vertex_a_index] - its.vertices[vertex_b_index]).norm());
}
}
return distance;
}
void find_support_areas() {
size_t next_group_id = 1;
for (const size_t &current_index : triangle_indexes_by_z) {
Triangle &current = triangles[current_index];
size_t group_id = 0;
float neighbourhood_unsupported_area = 0;
bool visited_neighbour = false;
std::queue<int> neighbours { };
std::set<int> explored { };
for (const auto &direct_neighbour_index : current.neighbours) {
if (direct_neighbour_index < 0 || !triangles[direct_neighbour_index].visited) {
continue;
}
neighbours.push(direct_neighbour_index);
const Triangle &direct_neighbour = triangles[direct_neighbour_index];
if (neighbourhood_unsupported_area <= direct_neighbour.unsupported_weight) {
neighbourhood_unsupported_area = direct_neighbour.unsupported_weight;
group_id = direct_neighbour.group_id;
}
visited_neighbour = true;
}
while (!neighbours.empty() && !visited_neighbour) {
int neighbour_index = neighbours.front();
neighbours.pop();
explored.insert(neighbour_index);
const Triangle &neighbour = triangles[neighbour_index];
if (explored.find(neighbour_index) != explored.end()
|| triangle_vertices_shortest_distance(mesh, current.index, neighbour_index)
> neighbour_max_distance) {
// not visited, already explored, or too far
continue;
}
if (neighbour.visited) {
visited_neighbour = true;
break;
}
for (const auto &neighbour_index : neighbour.neighbours) {
if (neighbour_index < 0) {
continue;
}
neighbours.push(neighbour_index);
}
}
current.visited = true;
current.unsupported_weight =
current.downward_dot_value >= limit_angle_cos ? current.area + neighbourhood_unsupported_area : 0;
current.group_id = group_id;
if (current.downward_dot_value > 0
&& (current.unsupported_weight > unsupported_area_limit || !visited_neighbour)) {
group_id = next_group_id;
next_group_id++;
std::queue<int> supporters { };
current.visited = false;
supporters.push(int(current_index));
float supported_size = 0;
while (supported_size < patch_size && !supporters.empty()) {
int s = supporters.front();
supporters.pop();
Triangle &supporter = triangles[s];
if (supporter.downward_dot_value <= 0.1) {
continue;
}
if (supporter.visited) {
supported_size += supporter.supports ? supporter.area : 0;
} else {
supporter.supports = true;
supporter.unsupported_weight = 0;
supported_size += supporter.area;
supporter.visited = true;
supporter.group_id = group_id;
for (const auto &n : supporter.neighbours) {
if (n < 0)
continue;
supporters.push(n);
}
}
}
}
}
}
#ifdef DEBUG_FILES
void debug_export() const {
Slic3r::CNumericLocalesSetter locales_setter;
{
std::string file_name = debug_out_path("groups.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < triangles.size(); ++i) {
Vec3f color = value_to_rgbf(0.0f, 19.0f, float(triangles[i].group_id % 20));
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", mesh.vertices[triangles[i].indices[index]](0),
mesh.vertices[triangles[i].indices[index]](1),
mesh.vertices[triangles[i].indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
{
std::string file_name = debug_out_path("sorted.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < triangle_indexes_by_z.size(); ++i) {
const Triangle &triangle = triangles[triangle_indexes_by_z[i]];
Vec3f color = Vec3f { float(i) / float(triangle_indexes_by_z.size()), float(i)
/ float(triangle_indexes_by_z.size()), 0.5 };
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", mesh.vertices[triangle.indices[index]](0),
mesh.vertices[triangle.indices[index]](1),
mesh.vertices[triangle.indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
{
std::string file_name = debug_out_path("weight.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < triangle_indexes_by_z.size(); ++i) {
const Triangle &triangle = triangles[triangle_indexes_by_z[i]];
Vec3f color = value_to_rgbf(0, 10, triangle.area);
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", mesh.vertices[triangle.indices[index]](0),
mesh.vertices[triangle.indices[index]](1),
mesh.vertices[triangle.indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
{
std::string file_name = debug_out_path("dot_value.obj");
FILE *fp = boost::nowide::fopen(file_name.c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "stl_write_obj: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < triangle_indexes_by_z.size(); ++i) {
const Triangle &triangle = triangles[triangle_indexes_by_z[i]];
Vec3f color = value_to_rgbf(-1, 1, triangle.downward_dot_value);
for (size_t index = 0; index < 3; ++index) {
fprintf(fp, "v %f %f %f %f %f %f\n", mesh.vertices[triangle.indices[index]](0),
mesh.vertices[triangle.indices[index]](1),
mesh.vertices[triangle.indices[index]](2), color(0),
color(1), color(2));
}
fprintf(fp, "f %zu %zu %zu\n", i * 3 + 1, i * 3 + 2, i * 3 + 3);
}
fclose(fp);
}
}
#endif
}
;
inline void do_experimental_support_placement(indexed_triangle_set mesh, TriangleSelectorGUI *selector,
float dot_limit) {
SupportPlacerMesh support_placer { std::move(mesh), dot_limit, 3, 6 };
support_placer.find_support_areas();
for (const Triangle &t : support_placer.triangles) {
if (t.supports) {
selector->set_facet(t.index, EnforcerBlockerType::ENFORCER);
selector->request_update_render_data();
}
}
support_placer.debug_export();
}
}

View File

@ -1,6 +1,7 @@
#include "GLGizmoFdmSupports.hpp" #include "GLGizmoFdmSupports.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/Subdivide.hpp"
//#include "slic3r/GUI/3DScene.hpp" //#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GLCanvas3D.hpp"
@ -10,15 +11,12 @@
#include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/format.hpp" #include "slic3r/GUI/format.hpp"
#include "slic3r/Utils/UndoRedo.hpp" #include "slic3r/Utils/UndoRedo.hpp"
#include "slic3r/GUI/Jobs/FDMSupportSpotsJob.hpp"
#include <GL/glew.h> #include <GL/glew.h>
namespace Slic3r::GUI { namespace Slic3r::GUI {
void GLGizmoFdmSupports::on_shutdown() void GLGizmoFdmSupports::on_shutdown()
{ {
m_highlight_by_angle_threshold_deg = 0.f; m_highlight_by_angle_threshold_deg = 0.f;
@ -26,44 +24,40 @@ void GLGizmoFdmSupports::on_shutdown()
m_parent.toggle_model_objects_visibility(true); m_parent.toggle_model_objects_visibility(true);
} }
std::string GLGizmoFdmSupports::on_get_name() const std::string GLGizmoFdmSupports::on_get_name() const
{ {
return _u8L("Paint-on supports"); return _u8L("Paint-on supports");
} }
bool GLGizmoFdmSupports::on_init() bool GLGizmoFdmSupports::on_init()
{ {
m_shortcut_key = WXK_CONTROL_L; m_shortcut_key = WXK_CONTROL_L;
m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
m_desc["reset_direction"] = _L("Reset direction"); m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Brush size") + ": "; m_desc["cursor_size"] = _L("Brush size") + ": ";
m_desc["cursor_type"] = _L("Brush shape") + ": "; m_desc["cursor_type"] = _L("Brush shape") + ": ";
m_desc["enforce_caption"] = _L("Left mouse button") + ": "; m_desc["enforce_caption"] = _L("Left mouse button") + ": ";
m_desc["enforce"] = _L("Enforce supports"); m_desc["enforce"] = _L("Enforce supports");
m_desc["block_caption"] = _L("Right mouse button") + ": "; m_desc["block_caption"] = _L("Right mouse button") + ": ";
m_desc["block"] = _L("Block supports"); m_desc["block"] = _L("Block supports");
m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": ";
m_desc["remove"] = _L("Remove selection"); m_desc["remove"] = _L("Remove selection");
m_desc["remove_all"] = _L("Remove all selection"); m_desc["remove_all"] = _L("Remove all selection");
m_desc["circle"] = _L("Circle"); m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere"); m_desc["sphere"] = _L("Sphere");
m_desc["pointer"] = _L("Triangles"); m_desc["pointer"] = _L("Triangles");
m_desc["highlight_by_angle"] = _L("Highlight overhang by angle"); m_desc["highlight_by_angle"] = _L("Highlight overhang by angle");
m_desc["enforce_button"] = _L("Enforce"); m_desc["enforce_button"] = _L("Enforce");
m_desc["cancel"] = _L("Cancel"); m_desc["cancel"] = _L("Cancel");
m_desc["tool_type"] = _L("Tool type") + ": "; m_desc["tool_type"] = _L("Tool type") + ": ";
m_desc["tool_brush"] = _L("Brush"); m_desc["tool_brush"] = _L("Brush");
m_desc["tool_smart_fill"] = _L("Smart fill"); m_desc["tool_smart_fill"] = _L("Smart fill");
m_desc["smart_fill_angle"] = _L("Smart fill angle"); m_desc["smart_fill_angle"] = _L("Smart fill angle");
m_desc["split_triangles"] = _L("Split triangles"); m_desc["split_triangles"] = _L("Split triangles");
m_desc["on_overhangs_only"] = _L("On overhangs only"); m_desc["on_overhangs_only"] = _L("On overhangs only");
return true; return true;
@ -71,7 +65,7 @@ bool GLGizmoFdmSupports::on_init()
void GLGizmoFdmSupports::render_painter_gizmo() void GLGizmoFdmSupports::render_painter_gizmo()
{ {
const Selection& selection = m_parent.get_selection(); const Selection &selection = m_parent.get_selection();
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
@ -84,29 +78,30 @@ void GLGizmoFdmSupports::render_painter_gizmo()
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit)
{ {
if (! m_c->selection_info()->model_object()) if (!m_c->selection_info()->model_object())
return; return;
const float approx_height = m_imgui->scaled(23.f); const float approx_height = m_imgui->scaled(23.f);
y = std::min(y, bottom_limit - approx_height); y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); m_imgui->begin(get_name(),
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.f); const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x
+ m_imgui->scaled(1.f);
const float autoset_slider_label_max_width = m_imgui->scaled(7.5f); const float autoset_slider_label_max_width = m_imgui->scaled(7.5f);
const float autoset_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle"), autoset_slider_label_max_width).x + m_imgui->scaled(1.f); const float autoset_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle"),
autoset_slider_label_max_width).x + m_imgui->scaled(1.f);
const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f); const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f); const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f); const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f);
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
@ -115,52 +110,110 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
const float buttons_width = std::max(button_enforce_width, button_cancel_width) + m_imgui->scaled(0.5f); const float buttons_width = std::max(button_enforce_width, button_cancel_width) + m_imgui->scaled(0.5f);
const float minimal_slider_width = m_imgui->scaled(4.f); const float minimal_slider_width = m_imgui->scaled(4.f);
const float tool_type_radio_left = m_imgui->calc_text_size(m_desc["tool_type"]).x + m_imgui->scaled(1.f); const float tool_type_radio_left = m_imgui->calc_text_size(m_desc["tool_type"]).x + m_imgui->scaled(1.f);
const float tool_type_radio_brush = m_imgui->calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f); const float tool_type_radio_brush = m_imgui->calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f);
const float tool_type_radio_smart_fill = m_imgui->calc_text_size(m_desc["tool_smart_fill"]).x + m_imgui->scaled(2.5f); const float tool_type_radio_smart_fill = m_imgui->calc_text_size(m_desc["tool_smart_fill"]).x
+ m_imgui->scaled(2.5f);
const float split_triangles_checkbox_width = m_imgui->calc_text_size(m_desc["split_triangles"]).x + m_imgui->scaled(2.5f); const float split_triangles_checkbox_width = m_imgui->calc_text_size(m_desc["split_triangles"]).x
const float on_overhangs_only_checkbox_width = m_imgui->calc_text_size(m_desc["on_overhangs_only"]).x + m_imgui->scaled(2.5f); + m_imgui->scaled(2.5f);
const float on_overhangs_only_checkbox_width = m_imgui->calc_text_size(m_desc["on_overhangs_only"]).x
+ m_imgui->scaled(2.5f);
float caption_max = 0.f; float caption_max = 0.f;
float total_text_max = 0.f; float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) { for (const auto &t : std::array<std::string, 3> { "enforce", "block", "remove" }) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
} }
total_text_max += caption_max + m_imgui->scaled(1.f); total_text_max += caption_max + m_imgui->scaled(1.f);
caption_max += m_imgui->scaled(1.f); caption_max += m_imgui->scaled(1.f);
const float sliders_left_width = std::max(std::max(autoset_slider_left, smart_fill_slider_left), std::max(cursor_slider_left, clipping_slider_left)); const float sliders_left_width = std::max(std::max(autoset_slider_left, smart_fill_slider_left),
const float slider_icon_width = m_imgui->get_slider_icon_size().x; std::max(cursor_slider_left, clipping_slider_left));
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width; const float slider_icon_width = m_imgui->get_slider_icon_size().x;
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
window_width = std::max(window_width, total_text_max); window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, button_width); window_width = std::max(window_width, button_width);
window_width = std::max(window_width, split_triangles_checkbox_width); window_width = std::max(window_width, split_triangles_checkbox_width);
window_width = std::max(window_width, on_overhangs_only_checkbox_width); window_width = std::max(window_width, on_overhangs_only_checkbox_width);
window_width = std::max(window_width, cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer); window_width = std::max(window_width,
cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer);
window_width = std::max(window_width, tool_type_radio_left + tool_type_radio_brush + tool_type_radio_smart_fill); window_width = std::max(window_width, tool_type_radio_left + tool_type_radio_brush + tool_type_radio_smart_fill);
window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f)); window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f));
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) {
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption);
ImGui::SameLine(caption_max); ImGui::SameLine(caption_max);
m_imgui->text(text); m_imgui->text(text);
}; };
for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) for (const auto &t : std::array<std::string, 3> { "enforce", "block", "remove" })
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
ImGui::Separator();
{
if (m_imgui->button("Subdivide")) {
const Selection &selection = m_parent.get_selection();
const ModelObject *mo = m_c->selection_info()->model_object();
for (ModelVolume *mv : mo->volumes)
if (mv->is_model_part()) {
auto new_its = its_subdivide(mv->mesh().its, 1.0f);
mv->set_mesh(new_its);
mv->set_new_unique_id();
}
update_model_object();
m_parent.set_as_dirty();
}
ImGui::NewLine();
std::string format_str = std::string("%.f") + I18N::translate_utf8("°",
"Degree sign to use in the respective slider in FDM supports gizmo,"
"placed after the number with no whitespace in between.");
m_imgui->slider_float("##angle_threshold_deg", &m_smart_support_limit_angle_deg, 0.f, 90.f, format_str.data(),
1.0f, true);
m_imgui->slider_float("##patch_size", &m_smart_support_patch_size, 0.f, 20.f, format_str.data(),
1.0f, false);
m_imgui->slider_float("##patch_spacing", &m_smart_support_patch_spacing, 0.f, 20.f, format_str.data(),
1.0f, false);
m_imgui->slider_float("##island_tolerance", &m_smart_support_islands_tolerance, 0.f, 10.f, format_str.data(),
1.0f, false);
ImGui::NewLine();
ImGui::SameLine(window_width - buttons_width - m_imgui->scaled(0.5f));
if (m_imgui->button(m_desc["enforce_button"], buttons_width, 0.f)) {
compute_smart_support_placement(m_smart_support_limit_angle_deg, m_smart_support_patch_size,
m_smart_support_patch_spacing, m_smart_support_islands_tolerance);
}
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"), UndoRedo::SnapshotType::GizmoAction);
ModelObject *mo = m_c->selection_info()->model_object();
int idx = -1;
for (ModelVolume *mv : mo->volumes)
if (mv->is_model_part()) {
++idx;
m_triangle_selectors[idx]->reset();
m_triangle_selectors[idx]->request_update_render_data();
}
update_model_object();
m_parent.set_as_dirty();
}
}
ImGui::Separator(); ImGui::Separator();
float position_before_text_y = ImGui::GetCursorPos().y; float position_before_text_y = ImGui::GetCursorPos().y;
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text_wrapped(m_desc["highlight_by_angle"] + ":", autoset_slider_label_max_width); m_imgui->text_wrapped(m_desc["highlight_by_angle"] + ":", autoset_slider_label_max_width);
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
float position_after_text_y = ImGui::GetCursorPos().y; float position_after_text_y = ImGui::GetCursorPos().y;
std::string format_str = std::string("%.f") + I18N::translate_utf8("°", std::string format_str = std::string("%.f") + I18N::translate_utf8("°",
"Degree sign to use in the respective slider in FDM supports gizmo," "Degree sign to use in the respective slider in FDM supports gizmo,"
"placed after the number with no whitespace in between."); "placed after the number with no whitespace in between.");
ImGui::SameLine(sliders_left_width); ImGui::SameLine(sliders_left_width);
float slider_height = m_imgui->get_slider_float_height(); float slider_height = m_imgui->get_slider_float_height();
@ -169,11 +222,15 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::SetCursorPosY(slider_start_position_y); ImGui::SetCursorPosY(slider_start_position_y);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
wxString tooltip = format_wxstr(_L("Preselects faces by overhang angle. It is possible to restrict paintable facets to only preselected faces when " wxString tooltip =
"the option \"%1%\" is enabled."), m_desc["on_overhangs_only"]); format_wxstr(
if (m_imgui->slider_float("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f, format_str.data(), 1.0f, true, tooltip)) { _L(
"Preselects faces by overhang angle. It is possible to restrict paintable facets to only preselected faces when "
"the option \"%1%\" is enabled."), m_desc["on_overhangs_only"]);
if (m_imgui->slider_float("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f,
format_str.data(), 1.0f, true, tooltip)) {
m_parent.set_slope_normal_angle(90.f - m_highlight_by_angle_threshold_deg); m_parent.set_slope_normal_angle(90.f - m_highlight_by_angle_threshold_deg);
if (! m_parent.is_using_slope()) { if (!m_parent.is_using_slope()) {
m_parent.use_slope(true); m_parent.use_slope(true);
m_parent.set_as_dirty(); m_parent.set_as_dirty();
} }
@ -186,7 +243,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->disabled_begin(m_highlight_by_angle_threshold_deg == 0.f); m_imgui->disabled_begin(m_highlight_by_angle_threshold_deg == 0.f);
ImGui::NewLine(); ImGui::NewLine();
ImGui::SameLine(window_width - 2.f*buttons_width - m_imgui->scaled(0.5f)); ImGui::SameLine(window_width - 2.f * buttons_width - m_imgui->scaled(0.5f));
if (m_imgui->button(m_desc["enforce_button"], buttons_width, 0.f)) { if (m_imgui->button(m_desc["enforce_button"], buttons_width, 0.f)) {
select_facets_by_angle(m_highlight_by_angle_threshold_deg, false); select_facets_by_angle(m_highlight_by_angle_threshold_deg, false);
m_highlight_by_angle_threshold_deg = 0.f; m_highlight_by_angle_threshold_deg = 0.f;
@ -199,13 +256,14 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
} }
m_imgui->disabled_end(); m_imgui->disabled_end();
ImGui::Separator(); ImGui::Separator();
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["tool_type"]); m_imgui->text(m_desc["tool_type"]);
float tool_type_offset = tool_type_radio_left + (window_width - tool_type_radio_left - tool_type_radio_brush - tool_type_radio_smart_fill + m_imgui->scaled(0.5f)) / 2.f; float tool_type_offset = tool_type_radio_left
+ (window_width - tool_type_radio_left - tool_type_radio_brush - tool_type_radio_smart_fill
+ m_imgui->scaled(0.5f)) / 2.f;
ImGui::SameLine(tool_type_offset); ImGui::SameLine(tool_type_offset);
ImGui::PushItemWidth(tool_type_radio_brush); ImGui::PushItemWidth(tool_type_radio_brush);
if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == ToolType::BRUSH)) if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == ToolType::BRUSH))
@ -220,11 +278,14 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_tool_type = ToolType::SMART_FILL; m_tool_type = ToolType::SMART_FILL;
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip(_L("Paints neighboring facets whose relative angle is less or equal to set angle."), max_tooltip_width); m_imgui->tooltip(_L("Paints neighboring facets whose relative angle is less or equal to set angle."),
max_tooltip_width);
m_imgui->checkbox(m_desc["on_overhangs_only"], m_paint_on_overhangs_only); m_imgui->checkbox(m_desc["on_overhangs_only"], m_paint_on_overhangs_only);
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip(format_wxstr(_L("Allows painting only on facets selected by: \"%1%\""), m_desc["highlight_by_angle"]), max_tooltip_width); m_imgui->tooltip(
format_wxstr(_L("Allows painting only on facets selected by: \"%1%\""), m_desc["highlight_by_angle"]),
max_tooltip_width);
ImGui::Separator(); ImGui::Separator();
@ -232,7 +293,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->text(m_desc.at("cursor_type")); m_imgui->text(m_desc.at("cursor_type"));
ImGui::NewLine(); ImGui::NewLine();
float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(1.5f)) / 2.f; float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle
- cursor_type_radio_pointer + m_imgui->scaled(1.5f)) / 2.f;
ImGui::SameLine(cursor_type_offset); ImGui::SameLine(cursor_type_offset);
ImGui::PushItemWidth(cursor_type_radio_sphere); ImGui::PushItemWidth(cursor_type_radio_sphere);
if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE)) if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
@ -259,18 +321,22 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip(_L("Paints only one facet."), max_tooltip_width); m_imgui->tooltip(_L("Paints only one facet."), max_tooltip_width);
m_imgui->disabled_begin(m_cursor_type != TriangleSelector::CursorType::SPHERE && m_cursor_type != TriangleSelector::CursorType::CIRCLE); m_imgui->disabled_begin(
m_cursor_type != TriangleSelector::CursorType::SPHERE
&& m_cursor_type != TriangleSelector::CursorType::CIRCLE);
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size")); m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_left_width); ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel")); m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true,
_L("Alt + Mouse wheel"));
m_imgui->checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled); m_imgui->checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled);
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
m_imgui->tooltip(_L("Splits bigger facets into smaller ones while the object is painted."), max_tooltip_width); m_imgui->tooltip(_L("Splits bigger facets into smaller ones while the object is painted."),
max_tooltip_width);
m_imgui->disabled_end(); m_imgui->disabled_end();
} else { } else {
@ -280,7 +346,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::SameLine(sliders_left_width); ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true, _L("Alt + Mouse wheel"))) if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax,
format_str.data(), 1.0f, true, _L("Alt + Mouse wheel")))
for (auto &triangle_selector : m_triangle_selectors) { for (auto &triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles(); triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data(); triangle_selector->request_update_render_data();
@ -294,9 +361,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
} }
else { else {
if (m_imgui->button(m_desc.at("reset_direction"))) { if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){ wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false); m_c->object_clipper()->set_position(-1., false);
}); });
} }
} }
@ -309,8 +376,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::Separator(); ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) { if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"), UndoRedo::SnapshotType::GizmoAction); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"), UndoRedo::SnapshotType::GizmoAction);
ModelObject *mo = m_c->selection_info()->model_object(); ModelObject *mo = m_c->selection_info()->model_object();
int idx = -1; int idx = -1;
for (ModelVolume *mv : mo->volumes) for (ModelVolume *mv : mo->volumes)
if (mv->is_model_part()) { if (mv->is_model_part()) {
++idx; ++idx;
@ -325,93 +392,111 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->end(); m_imgui->end();
} }
void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
{ {
float threshold = (float(M_PI)/180.f)*threshold_deg; float threshold = (float(M_PI) / 180.f) * threshold_deg;
const Selection& selection = m_parent.get_selection(); const Selection &selection = m_parent.get_selection();
const ModelObject* mo = m_c->selection_info()->model_object(); const ModelObject *mo = m_c->selection_info()->model_object();
const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
int mesh_id = -1; int mesh_id = -1;
for (const ModelVolume* mv : mo->volumes) { for (const ModelVolume *mv : mo->volumes) {
if (! mv->is_model_part()) if (!mv->is_model_part())
continue; continue;
++mesh_id; ++mesh_id;
const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true);
Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast<float>().normalized(); Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast<float>().normalized();
Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast<float>().normalized(); Vec3f limit =
(trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast<float>().normalized();
float dot_limit = limit.dot(down); float dot_limit = limit.dot(down);
indexed_triangle_set mesh_triangles = mv->mesh().its;
its_transform(mesh_triangles, mi->get_matrix(true));
do_experimental_support_placement(std::move(mesh_triangles), m_triangle_selectors[mesh_id].get(), dot_limit);
if (false) {
// Now calculate dot product of vert_direction and facets' normals. // Now calculate dot product of vert_direction and facets' normals.
int idx = 0; int idx = 0;
const indexed_triangle_set &its = mv->mesh().its; const indexed_triangle_set &its = mv->mesh().its;
for (const stl_triangle_vertex_indices &face : its.indices) { for (const stl_triangle_vertex_indices &face : its.indices) {
if (its_face_normal(its, face).dot(down) > dot_limit) { if (its_face_normal(its, face).dot(down) > dot_limit) {
m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER); m_triangle_selectors[mesh_id]->set_facet(idx,
block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER);
m_triangle_selectors.back()->request_update_render_data(); m_triangle_selectors.back()->request_update_render_data();
} }
++ idx; ++idx;
}
} }
} }
Plater::TakeSnapshot snapshot(wxGetApp().plater(), block ? _L("Block supports by angle") Plater::TakeSnapshot snapshot(wxGetApp().plater(), block ? _L("Block supports by angle")
: _L("Add supports by angle")); :
_L("Add supports by angle"));
update_model_object(); update_model_object();
m_parent.set_as_dirty(); m_parent.set_as_dirty();
} }
void GLGizmoFdmSupports::compute_smart_support_placement(float limit_angle_deg, float patch_size, float patch_spacing, float islands_tolerance) {
float threshold = (float(M_PI) / 180.f) * limit_angle_deg;
FDMSupportSpotsConfig support_spots_config {
threshold, patch_size, patch_spacing, islands_tolerance
};
const Selection &selection = m_parent.get_selection();
const ModelObject *mo = m_c->selection_info()->model_object();
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
std::unordered_map<size_t, SupportedMeshData> data { };
for (const ModelVolume *mv : mo->volumes) {
if (!mv->is_model_part())
continue;
Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true);
SupportedMeshData smd { mv->mesh().its, trafo_matrix };
data.emplace(mv->id().id, smd);
}
Plater *plater = wxGetApp().plater();
std::unique_ptr<FDMSupportSpotsJob> job = std::make_unique<FDMSupportSpotsJob>(plater, support_spots_config,
mo->id(), mi->id(), data);
queue_job(plater->get_ui_job_worker(), std::move(job));
}
void GLGizmoFdmSupports::update_model_object() const void GLGizmoFdmSupports::update_model_object() const
{ {
bool updated = false; bool updated = false;
ModelObject* mo = m_c->selection_info()->model_object(); ModelObject *mo = m_c->selection_info()->model_object();
int idx = -1; int idx = -1;
for (ModelVolume* mv : mo->volumes) { for (ModelVolume *mv : mo->volumes) {
if (! mv->is_model_part()) if (!mv->is_model_part())
continue; continue;
++idx; ++idx;
updated |= mv->supported_facets.set(*m_triangle_selectors[idx].get()); updated |= mv->supported_facets.set(*m_triangle_selectors[idx].get());
} }
if (updated) { if (updated) {
const ModelObjectPtrs& mos = wxGetApp().model().objects; const ModelObjectPtrs &mos = wxGetApp().model().objects;
wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin());
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
} }
} }
void GLGizmoFdmSupports::update_from_model_object() void GLGizmoFdmSupports::update_from_model_object()
{ {
wxBusyCursor wait; wxBusyCursor wait;
const ModelObject* mo = m_c->selection_info()->model_object(); const ModelObject *mo = m_c->selection_info()->model_object();
m_triangle_selectors.clear(); m_triangle_selectors.clear();
int volume_id = -1; int volume_id = -1;
for (const ModelVolume* mv : mo->volumes) { for (const ModelVolume *mv : mo->volumes) {
if (! mv->is_model_part()) if (!mv->is_model_part())
continue; continue;
++volume_id; ++volume_id;
// This mesh does not account for the possible Z up SLA offset. // This mesh does not account for the possible Z up SLA offset.
const TriangleMesh* mesh = &mv->mesh(); const TriangleMesh *mesh = &mv->mesh();
m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh)); m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
// Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize(). // Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize().
@ -420,15 +505,13 @@ void GLGizmoFdmSupports::update_from_model_object()
} }
} }
PainterGizmoType GLGizmoFdmSupports::get_painter_type() const PainterGizmoType GLGizmoFdmSupports::get_painter_type() const
{ {
return PainterGizmoType::FDM_SUPPORTS; return PainterGizmoType::FDM_SUPPORTS;
} }
wxString GLGizmoFdmSupports::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const wxString GLGizmoFdmSupports::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
{ {
wxString action_name; wxString action_name;
if (shift_down) if (shift_down)
action_name = _L("Remove selection"); action_name = _L("Remove selection");

View File

@ -3,8 +3,6 @@
#include "GLGizmoPainterBase.hpp" #include "GLGizmoPainterBase.hpp"
#include "Experiment.cpp"
namespace Slic3r::GUI { namespace Slic3r::GUI {
class GLGizmoFdmSupports : public GLGizmoPainterBase class GLGizmoFdmSupports : public GLGizmoPainterBase
@ -38,9 +36,16 @@ private:
void select_facets_by_angle(float threshold, bool block); void select_facets_by_angle(float threshold, bool block);
void compute_smart_support_placement(float limit_angle_deg, float patch_size, float patch_spacing, float islands_tolerance);
// This map holds all translated description texts, so they can be easily referenced during layout calculations // This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc; std::map<std::string, wxString> m_desc;
float m_smart_support_limit_angle_deg = 35.0f;
float m_smart_support_patch_size = 6.0f;
float m_smart_support_patch_spacing = 6.0f;
float m_smart_support_islands_tolerance = 1.0f;
}; };

View File

@ -0,0 +1,102 @@
#include "FDMSupportSpotsJob.hpp"
#include "libslic3r/TriangleSelector.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp"
namespace Slic3r::GUI {
FDMSupportSpotsJob::FDMSupportSpotsJob(Plater *plater,
FDMSupportSpotsConfig support_spots_config,
ObjectID model_object_id,
ObjectID model_instance_id,
std::unordered_map<size_t, SupportedMeshData> model_volumes_data) :
m_plater(plater), m_support_spots_config(support_spots_config), m_model_object_id(model_object_id), m_model_instance_id(
model_instance_id),
m_model_volumes_data(model_volumes_data)
{
}
void FDMSupportSpotsJob::process(Ctl &ctl) {
if (ctl.was_canceled()) {
return;
}
auto status_text_preparing = _u8L("Densifying mesh and preparing search structures");
auto status_text_computing = _u8L("Computing support placement");
auto status_text_canceled = _u8L("Support placement computation canceled");
auto status_text_done = _u8L("Support placement computation finished");
int step_size = 100 / (this->m_model_volumes_data.size() * 2);
int status = 0;
for (auto &data : this->m_model_volumes_data) {
if (ctl.was_canceled()) {
ctl.update_status(100, status_text_canceled);
return;
}
ctl.update_status(status, status_text_preparing);
status += step_size;
FDMSupportSpots support_spots_alg { m_support_spots_config, data.second.mesh, data.second.transform };
if (ctl.was_canceled()) {
ctl.update_status(100, status_text_canceled);
return;
}
ctl.update_status(status, status_text_computing);
status += step_size;
support_spots_alg.find_support_areas();
std::vector<size_t> supported_face_indexes { };
for (const auto &triangle : support_spots_alg.m_triangles) {
if (triangle.supports) {
supported_face_indexes.push_back(triangle.index);
}
}
this->m_computed_support_data.emplace(data.first, supported_face_indexes);
}
if (ctl.was_canceled()) {
ctl.update_status(100, status_text_canceled);
return;
}
ctl.update_status(100, status_text_canceled);
}
void FDMSupportSpotsJob::finalize(bool canceled, std::exception_ptr &exception) {
if (canceled || exception)
return;
ModelObject *model_object = nullptr;
for (ModelObject *mo : this->m_plater->model().objects) {
if (mo->id() == this->m_model_object_id) {
model_object = mo;
break;
}
}
if (model_object == nullptr)
return;
ModelInstance *model_instance = nullptr;
for (ModelInstance *mi : model_object->instances) {
if (mi->id() == this->m_model_instance_id) {
model_instance = mi;
break;
}
}
if (model_instance == nullptr)
return;
for (ModelVolume *mv : model_object->volumes) {
auto fdm_support_spots_result = this->m_computed_support_data.find(mv->id().id);
if (fdm_support_spots_result != this->m_computed_support_data.end()) {
TriangleSelector selector { mv->mesh() };
for (size_t face_index : fdm_support_spots_result->second) {
selector.set_facet(face_index, EnforcerBlockerType::ENFORCER);
}
mv->supported_facets.set(selector);
}
}
}
}

View File

@ -0,0 +1,41 @@
#ifndef SRC_SLIC3R_GUI_JOBS_FDMSUPPORTSPOTSJOB_HPP_
#define SRC_SLIC3R_GUI_JOBS_FDMSUPPORTSPOTSJOB_HPP_
#include "Job.hpp"
#include "libslic3r/FDMSupportSpots.hpp"
#include "slic3r/GUI/Plater.hpp"
namespace Slic3r::GUI {
struct SupportedMeshData {
indexed_triangle_set mesh;
Transform3d transform;
};
class FDMSupportSpotsJob: public Job {
Plater *m_plater;
FDMSupportSpotsConfig m_support_spots_config;
ObjectID m_model_object_id;
ObjectID m_model_instance_id;
//map of model volume ids and corresponding mesh data
std::unordered_map<size_t, SupportedMeshData> m_model_volumes_data;
//vector of supported face indexes for each model volume
std::unordered_map<size_t, std::vector<size_t>> m_computed_support_data;
public:
FDMSupportSpotsJob(Plater *plater,
FDMSupportSpotsConfig support_spots_config,
ObjectID model_object_id,
ObjectID model_instance_id,
std::unordered_map<size_t, SupportedMeshData> model_volumes_data);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &exception) override;
};
}
#endif /* SRC_SLIC3R_GUI_JOBS_FDMSUPPORTSPOTSJOB_HPP_ */