Merge pull request #6 from Prusa-Development/pm_support_spots_generator

Pm support spots generator
This commit is contained in:
Pavel Mikuš 2022-09-09 15:33:33 +02:00 committed by GitHub
commit 36c951501e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 1611 additions and 8 deletions

View file

@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 78
ColumnLimit: 140
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true

View file

@ -1,8 +1,6 @@
#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
#include "libslic3r/Point.hpp"
#include "libslic3r/EdgeGrid.hpp"
#include "libslic3r/AABBTreeIndirect.hpp"
#include "libslic3r/Line.hpp"

View file

@ -245,6 +245,8 @@ set(SLIC3R_SOURCES
SlicingAdaptive.hpp
Subdivide.cpp
Subdivide.hpp
SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp
SupportMaterial.cpp
SupportMaterial.hpp
Surface.cpp
@ -277,6 +279,8 @@ set(SLIC3R_SOURCES
TriangleSelector.hpp
TriangleSetSampling.cpp
TriangleSetSampling.hpp
TriangleSelectorWrapper.cpp
TriangleSelectorWrapper.hpp
MTUtils.hpp
Zipper.hpp
Zipper.cpp

View file

@ -5,6 +5,7 @@
#include "libslic3r/format.hpp"
#include "GCodeProcessor.hpp"
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>

View file

@ -825,6 +825,8 @@ void Print::process()
obj->infill();
for (PrintObject *obj : m_objects)
obj->ironing();
for (PrintObject *obj : m_objects)
obj->generate_support_spots();
for (PrintObject *obj : m_objects)
obj->generate_support_material();
if (this->set_started(psWipeTower)) {

View file

@ -61,7 +61,7 @@ enum PrintStep : unsigned int {
enum PrintObjectStep : unsigned int {
posSlice, posPerimeters, posPrepareInfill,
posInfill, posIroning, posSupportMaterial, posCount,
posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posCount,
};
// A PrintRegion object represents a group of volumes to print
@ -381,6 +381,7 @@ private:
void prepare_infill();
void infill();
void ironing();
void generate_support_spots();
void generate_support_material();
void slice_volumes();

View file

@ -1195,8 +1195,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
update_apply_status(false);
}
// Invalidate just the supports step.
for (const PrintObjectStatus &print_object_status : print_objects_range)
for (const PrintObjectStatus &print_object_status : print_objects_range) {
update_apply_status(print_object_status.print_object->invalidate_step(posSupportSpotsSearch));
update_apply_status(print_object_status.print_object->invalidate_step(posSupportMaterial));
}
if (supports_differ) {
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);

View file

@ -17,6 +17,9 @@
#include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp"
#include "Format/STL.hpp"
#include "SupportSpotsGenerator.hpp"
#include "TriangleSelectorWrapper.hpp"
#include "format.hpp"
#include <float.h>
#include <string_view>
@ -394,6 +397,65 @@ void PrintObject::ironing()
}
}
/*
std::vector<size_t> problematic_layers = SupportSpotsGenerator::quick_search(this);
if (!problematic_layers.empty()) {
std::cout << "Object needs supports" << std::endl;
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
L("Supportable issues found. Consider enabling supports for this object"));
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
L("Supportable issues found. Consider enabling supports for this object"));
for (size_t index = 0; index < std::min(problematic_layers.size(), size_t(4)); ++index) {
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
format(L("Layer with issues: %1%"), problematic_layers[index] + 1));
}
}
*/
void PrintObject::generate_support_spots()
{
if (this->set_started(posSupportSpotsSearch)) {
BOOST_LOG_TRIVIAL(debug)
<< "Searching support spots - start";
m_print->set_status(75, L("Searching support spots"));
if (this->m_config.support_material && !this->m_config.support_material_auto &&
std::all_of(this->model_object()->volumes.begin(), this->model_object()->volumes.end(),
[](const ModelVolume* mv){return mv->supported_facets.empty();})
) {
SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values};
SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this, params);
auto obj_transform = this->trafo_centered();
for (ModelVolume *model_volume : this->model_object()->volumes) {
if (model_volume->is_model_part()) {
Transform3d mesh_transformation = obj_transform * model_volume->get_matrix();
Transform3d inv_transform = mesh_transformation.inverse();
TriangleSelectorWrapper selector { model_volume->mesh(), mesh_transformation};
for (const SupportSpotsGenerator::SupportPoint &support_point : issues.support_points) {
Vec3f point = Vec3f(inv_transform.cast<float>() * support_point.position);
Vec3f origin = Vec3f(
inv_transform.cast<float>() * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f));
selector.enforce_spot(point, origin, support_point.spot_radius);
}
model_volume->supported_facets.set(selector.selector);
#if 0 //DEBUG export
indexed_triangle_set copy = model_volume->mesh().its;
its_transform(copy, obj_transform * model_transformation);
its_write_obj(copy,
debug_out_path(("model"+std::to_string(model_volume->id().id)+".obj").c_str()).c_str());
#endif
}
}
}
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug)
<< "Searching support spots - end";
this->set_done(posSupportSpotsSearch);
}
}
void PrintObject::generate_support_material()
{
if (this->set_started(posSupportMaterial)) {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,82 @@
#ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_
#define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_
#include "libslic3r/Print.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
namespace SupportSpotsGenerator {
struct Params {
Params(const std::vector<std::string> &filament_types) {
if (filament_types.size() > 1) {
BOOST_LOG_TRIVIAL(warning)
<< "SupportSpotsGenerator does not currently handle different materials properly, only first will be used";
}
if (filament_types.empty() || filament_types[0].empty()) {
BOOST_LOG_TRIVIAL(error)
<< "SupportSpotsGenerator error: empty filament_type";
filament_type = std::string("PLA");
} else {
filament_type = filament_types[0];
}
}
// the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2]
const float bridge_distance = 12.0f; //mm
const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) )
const float overhang_angle_deg = 80.0f;
const std::pair<float,float> malformation_angle_span_deg = std::pair<float, float> { 45.0f, 80.0f };
const float min_distance_between_support_points = 3.0f; //mm
const float support_points_interface_radius = 1.5f; // mm
const float connections_min_considerable_area = 1.5f; //mm^2
const float small_parts_threshold = 5.0f; //mm^3
const float small_parts_support_points_interface_radius = 3.0f; // mm
std::string filament_type;
const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position.
const float max_acceleration = 9 * 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low)
const double filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important
const double material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials.
const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... );
const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments
// MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface
double get_bed_adhesion_yield_strength() const {
if (filament_type == "PLA") {
return 0.018 * 1e6;
} else if (filament_type == "PET" || filament_type == "PETG") {
return 0.3 * 1e6;
} else { //PLA default value - defensive approach, PLA has quite low adhesion
return 0.018 * 1e6;
}
}
//just return PLA adhesion value as value for supports
double get_support_spots_adhesion_strength() const {
return 0.018f * 1e6;
}
};
struct SupportPoint {
SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec3f &direction);
Vec3f position;
float force;
float spot_radius;
Vec3f direction;
};
struct Issues {
std::vector<SupportPoint> support_points;
};
// std::vector<size_t> quick_search(const PrintObject *po, const Params &params);
Issues full_search(const PrintObject *po, const Params &params);
}
}
#endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */

View file

@ -0,0 +1,46 @@
#include "TriangleSelectorWrapper.hpp"
#include <memory>
namespace Slic3r {
TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh, const Transform3d& mesh_transform) :
mesh(mesh), mesh_transform(mesh_transform), selector(mesh), triangles_tree(
AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) {
}
void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &origin, float radius) {
std::vector<igl::Hit> hits;
Vec3f dir = (point - origin).normalized();
if (AABBTreeIndirect::intersect_ray_all_hits(mesh.its.vertices, mesh.its.indices, triangles_tree,
Vec3d(origin.cast<double>()),
Vec3d(dir.cast<double>()),
hits)) {
for (int hit_idx = hits.size() - 1; hit_idx >= 0; --hit_idx) {
const igl::Hit &hit = hits[hit_idx];
Vec3f pos = origin + dir * hit.t;
Vec3f face_normal = its_face_normal(mesh.its, hit.id);
if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) {
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
pos, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { });
selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(),
true, 0.0f);
break;
}
}
} else {
size_t hit_idx_out;
Vec3f hit_point_out;
float dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices,
triangles_tree, point, hit_idx_out, hit_point_out);
if (dist < radius) {
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
point, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { });
selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER,
Transform3d::Identity(),
true, 0.0f);
}
}
}
}

View file

@ -0,0 +1,31 @@
#ifndef SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_
#define SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_
#include "TriangleSelector.hpp"
#include "Model.hpp"
#include "AABBTreeIndirect.hpp"
namespace Slic3r {
//NOTE: We need to replace the FacetsAnnotation struct for support storage (or extend/add another)
// Problems: Does not support negative volumes, strange usage for supports computed from extrusion -
// expensively converted back to triangles and then sliced again.
// Another problem is weird and very limited interface when painting supports via algorithms
class TriangleSelectorWrapper {
public:
const TriangleMesh &mesh;
const Transform3d& mesh_transform;
TriangleSelector selector;
AABBTreeIndirect::Tree<3, float> triangles_tree;
TriangleSelectorWrapper(const TriangleMesh &mesh, const Transform3d& mesh_transform);
void enforce_spot(const Vec3f &point, const Vec3f& origin, float radius);
};
}
#endif /* SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ */

View file

@ -10,6 +10,8 @@
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/Print.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include <GL/glew.h>
@ -39,6 +41,8 @@ bool GLGizmoFdmSupports::on_init()
{
m_shortcut_key = WXK_CONTROL_L;
m_desc["auto_generate"] = _L("Auto-generate supports");
m_desc["generating"] = _L("Generating supports...");
m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Brush size") + ": ";
@ -91,7 +95,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (! m_c->selection_info()->model_object())
return;
const float approx_height = m_imgui->scaled(23.f);
const float approx_height = m_imgui->scaled(25.f);
y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
@ -153,6 +157,15 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::Separator();
if (waiting_for_autogenerated_supports) {
m_imgui->text(m_desc.at("generating"));
} else {
bool generate = m_imgui->button(m_desc.at("auto_generate"));
if (generate)
auto_generate();
}
ImGui::Separator();
float position_before_text_y = ImGui::GetCursorPos().y;
ImGui::AlignTextToFramePadding();
m_imgui->text_wrapped(m_desc["highlight_by_angle"] + ":", autoset_slider_label_max_width);
@ -319,6 +332,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
}
update_model_object();
this->waiting_for_autogenerated_supports = false;
m_parent.set_as_dirty();
}
@ -366,9 +380,54 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
Plater::TakeSnapshot snapshot(wxGetApp().plater(), block ? _L("Block supports by angle")
: _L("Add supports by angle"));
update_model_object();
this->waiting_for_autogenerated_supports = false;
m_parent.set_as_dirty();
}
void GLGizmoFdmSupports::data_changed()
{
GLGizmoPainterBase::data_changed();
if (! m_c->selection_info())
return;
ModelObject* mo = m_c->selection_info()->model_object();
if (mo && this->waiting_for_autogenerated_supports) {
get_data_from_backend();
} else {
this->waiting_for_autogenerated_supports = false;
}
}
void GLGizmoFdmSupports::get_data_from_backend()
{
if (! has_backend_supports())
return;
ModelObject* mo = m_c->selection_info()->model_object();
// find the respective PrintObject, we need a pointer to it
for (const PrintObject* po : m_parent.fff_print()->objects()) {
if (po->model_object()->id() == mo->id()) {
std::unordered_map<size_t, const ModelVolume*> mvs;
for (const ModelVolume* mv : po->model_object()->volumes) {
if (mv->is_model_part()) {
mvs.emplace(mv->id().id, mv);
}
}
int mesh_id = -1.0f;
for (ModelVolume* mv : mo->volumes){
if (mv->is_model_part()){
mesh_id++;
mv->supported_facets.assign(mvs[mv->id().id]->supported_facets);
m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true);
m_triangle_selectors[mesh_id]->request_update_render_data();
}
}
this->waiting_for_autogenerated_supports = false;
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
m_parent.set_as_dirty();
}
}
}
void GLGizmoFdmSupports::update_model_object() const
@ -391,8 +450,6 @@ void GLGizmoFdmSupports::update_model_object() const
}
}
void GLGizmoFdmSupports::update_from_model_object()
{
wxBusyCursor wait;
@ -417,6 +474,58 @@ void GLGizmoFdmSupports::update_from_model_object()
}
}
bool GLGizmoFdmSupports::has_backend_supports() const
{
const ModelObject* mo = m_c->selection_info()->model_object();
if (! mo)
return false;
// find PrintObject with this ID
for (const PrintObject* po : m_parent.fff_print()->objects()) {
if (po->model_object()->id() == mo->id())
return po->is_step_done(posSupportSpotsSearch);
}
return false;
}
void GLGizmoFdmSupports::reslice_FDM_supports(bool postpone_error_messages) const {
wxGetApp().CallAfter(
[this, postpone_error_messages]() {
wxGetApp().plater()->reslice_FFF_until_step(posSupportSpotsSearch,
*m_c->selection_info()->model_object(), postpone_error_messages);
});
}
void GLGizmoFdmSupports::auto_generate()
{
ModelObject *mo = m_c->selection_info()->model_object();
bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){
return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty();
});
MessageDialog dlg(GUI::wxGetApp().plater(),
_L("Autogeneration will erase all currently painted areas.") + "\n\n" +
_L("Are you sure you want to do it?") + "\n",
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (not_painted || dlg.ShowModal() == wxID_YES) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points"));
int mesh_id = -1.0f;
for (ModelVolume *mv : mo->volumes) {
if (mv->is_model_part()) {
mesh_id++;
mv->supported_facets.reset();
m_triangle_selectors[mesh_id]->reset();
m_triangle_selectors[mesh_id]->request_update_render_data();
}
}
mo->config.set("support_material", true);
mo->config.set("support_material_auto", false);
this->waiting_for_autogenerated_supports = true;
wxGetApp().CallAfter([this]() { reslice_FDM_supports(); });
}
}
PainterGizmoType GLGizmoFdmSupports::get_painter_type() const

View file

@ -26,6 +26,7 @@ protected:
private:
bool on_init() override;
void data_changed() override;
void update_model_object() const override;
void update_from_model_object() override;
@ -39,6 +40,13 @@ private:
// 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.
std::map<std::string, wxString> m_desc;
bool waiting_for_autogenerated_supports = false;
bool has_backend_supports() const;
void reslice_FDM_supports(bool postpone_error_messages = false) const;
void auto_generate();
void get_data_from_backend();
};