mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 21:45:57 +08:00
Refactoring - running the support placement in gui worker
debug UI debug subdivide
This commit is contained in:
parent
d265d781d4
commit
2b9741da68
@ -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
|
||||||
|
297
src/libslic3r/FDMSupportSpots.cpp
Normal file
297
src/libslic3r/FDMSupportSpots.cpp
Normal 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 ¤t_index : this->m_triangle_indexes_by_z) {
|
||||||
|
Triangle ¤t = 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
73
src/libslic3r/FDMSupportSpots.hpp
Normal file
73
src/libslic3r/FDMSupportSpots.hpp
Normal 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
218
src/libslic3r/Subdivide.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/libslic3r/Subdivide.hpp
Normal file
12
src/libslic3r/Subdivide.hpp
Normal 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_
|
@ -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
|
||||||
|
@ -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 ¤t_index : triangle_indexes_by_z) {
|
|
||||||
Triangle ¤t = 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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,15 +24,11 @@ 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;
|
||||||
@ -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,26 +78,27 @@ 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);
|
||||||
@ -117,40 +112,98 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||||||
|
|
||||||
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),
|
||||||
|
std::max(cursor_slider_left, clipping_slider_left));
|
||||||
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
||||||
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
|
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;
|
||||||
@ -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 =
|
||||||
|
format_wxstr(
|
||||||
|
_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"]);
|
"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)) {
|
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,7 +361,7 @@ 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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -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");
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
102
src/slic3r/GUI/Jobs/FDMSupportSpotsJob.cpp
Normal file
102
src/slic3r/GUI/Jobs/FDMSupportSpotsJob.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
src/slic3r/GUI/Jobs/FDMSupportSpotsJob.hpp
Normal file
41
src/slic3r/GUI/Jobs/FDMSupportSpotsJob.hpp
Normal 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_ */
|
Loading…
x
Reference in New Issue
Block a user