Merge remote-tracking branch 'origin/et_outofbed_optimization'

This commit is contained in:
enricoturri1966 2023-05-12 12:01:51 +02:00
commit a430aa41ba
10 changed files with 367 additions and 173 deletions

View File

@ -469,11 +469,13 @@ std::string Print::validate(std::string* warning) const
return _u8L("The supplied settings will cause an empty print."); return _u8L("The supplied settings will cause an empty print.");
if (m_config.complete_objects) { if (m_config.complete_objects) {
if (! sequential_print_horizontal_clearance_valid(*this)) if (!sequential_print_horizontal_clearance_valid(*this, const_cast<Polygons*>(&m_sequential_print_clearance_contours)))
return _u8L("Some objects are too close; your extruder will collide with them."); return _u8L("Some objects are too close; your extruder will collide with them.");
if (! sequential_print_vertical_clearance_valid(*this)) if (!sequential_print_vertical_clearance_valid(*this))
return _u8L("Some objects are too tall and cannot be printed without extruder collisions."); return _u8L("Some objects are too tall and cannot be printed without extruder collisions.");
} }
else
const_cast<Polygons*>(&m_sequential_print_clearance_contours)->clear();
if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) { if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) {
return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together."); return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together.");

View File

@ -609,6 +609,7 @@ public:
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; }
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
protected: protected:
@ -658,6 +659,9 @@ private:
// Estimated print time, filament consumed. // Estimated print time, filament consumed.
PrintStatistics m_print_statistics; PrintStatistics m_print_statistics;
// Cache to store sequential print clearance contours
Polygons m_sequential_print_clearance_contours;
// To allow GCode to set the Print's GCodeExport step status. // To allow GCode to set the Print's GCodeExport step status.
friend class GCode; friend class GCode;
// To allow GCodeProcessor to emit warnings. // To allow GCodeProcessor to emit warnings.

View File

@ -833,64 +833,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
} }
} }
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
{
const Model& model = GUI::wxGetApp().plater()->model();
auto volume_below = [](GLVolume& volume) -> bool
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
auto volume_sinking = [](GLVolume& volume) -> bool
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
// Cached bounding box of a volume above the print bed.
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
// Cached 3D convex hull of a volume above the print bed.
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
bool contained_min_one = false;
for (GLVolume* volume : this->volumes)
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
BuildVolume::ObjectState state;
if (volume_below(*volume))
state = BuildVolume::ObjectState::Below;
else {
switch (build_volume.type()) {
case BuildVolume::Type::Rectangle:
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
state = build_volume.volume_state_bbox(volume_bbox(*volume));
break;
case BuildVolume::Type::Circle:
case BuildVolume::Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case BuildVolume::Type::Custom:
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
break;
default:
// Ignore, don't produce any collision.
state = BuildVolume::ObjectState::Inside;
break;
}
assert(state != BuildVolume::ObjectState::Below);
}
volume->is_outside = state != BuildVolume::ObjectState::Inside;
if (volume->printable) {
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
overall_state = ModelInstancePVS_Fully_Outside;
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
overall_state = ModelInstancePVS_Partly_Outside;
contained_min_one |= !volume->is_outside;
}
}
if (out_state != nullptr)
*out_state = overall_state;
return contained_min_one;
}
void GLVolumeCollection::reset_outside_state() void GLVolumeCollection::reset_outside_state()
{ {
for (GLVolume* volume : this->volumes) { for (GLVolume* volume : this->volumes) {

View File

@ -450,9 +450,6 @@ public:
void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; } void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; }
void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; } void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; }
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
void reset_outside_state(); void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config); void update_colors_by_extruder(const DynamicPrintConfig* config);

View File

@ -880,20 +880,22 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas
ImGui::PopStyleVar(2); ImGui::PopStyleVar(2);
} }
void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons) void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours, bool generate_fill)
{ {
m_perimeter.reset(); m_contours.clear();
m_instances.clear();
m_fill.reset(); m_fill.reset();
if (polygons.empty())
if (contours.empty())
return; return;
if (m_render_fill) { if (generate_fill) {
GLModel::Geometry fill_data; GLModel::Geometry fill_data;
fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f };
// vertices + indices // vertices + indices
const ExPolygons polygons_union = union_ex(polygons); const ExPolygons polygons_union = union_ex(contours.contours);
unsigned int vertices_counter = 0; unsigned int vertices_counter = 0;
for (const ExPolygon& poly : polygons_union) { for (const ExPolygon& poly : polygons_union) {
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly); const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly);
@ -906,17 +908,48 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons
fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
} }
} }
m_fill.init_from(std::move(fill_data)); m_fill.init_from(std::move(fill_data));
} }
m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting for (size_t i = 0; i < contours.contours.size(); ++i) {
GLModel& model = m_contours.emplace_back(GLModel());
model.init_from(contours.contours[i], 0.025f); // add a small positive z to avoid z-fighting
}
if (contours.trafos.has_value()) {
// create the requested instances
for (const auto& instance : *contours.trafos) {
m_instances.emplace_back(instance.first, instance.second);
}
}
else {
// no instances have been specified
// create one instance for every polygon
for (size_t i = 0; i < contours.contours.size(); ++i) {
m_instances.emplace_back(i, Transform3f::Identity());
}
}
}
void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector<Transform3d>& trafos)
{
if (trafos.size() == m_instances.size()) {
for (size_t i = 0; i < trafos.size(); ++i) {
m_instances[i].second = trafos[i];
}
}
else
assert(false);
} }
void GLCanvas3D::SequentialPrintClearance::render() void GLCanvas3D::SequentialPrintClearance::render()
{ {
const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f };
const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f };
if (m_contours.empty() || m_instances.empty())
return;
GLShaderProgram* shader = wxGetApp().get_shader("flat"); GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr) if (shader == nullptr)
@ -933,10 +966,34 @@ void GLCanvas3D::SequentialPrintClearance::render()
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
m_perimeter.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR);
m_perimeter.render();
m_fill.render(); m_fill.render();
#if ENABLE_GL_CORE_PROFILE
if (OpenGLManager::get_gl_info().is_core_profile()) {
shader->stop_using();
shader = wxGetApp().get_shader("dashed_thick_lines");
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const std::array<int, 4>& viewport = camera.get_viewport();
shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3])));
shader->set_uniform("width", 1.0f);
shader->set_uniform("gap_size", 0.0f);
}
else
#endif // ENABLE_GL_CORE_PROFILE
glsafe(::glLineWidth(2.0f));
for (const auto& [id, trafo] : m_instances) {
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo);
assert(id < m_contours.size());
m_contours[id].set_color(m_fill.is_initialized() ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR);
m_contours[id].render();
}
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
@ -1453,11 +1510,85 @@ void GLCanvas3D::reset_volumes()
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
{ {
ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside; ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside;
if (m_initialized) if (m_initialized && !m_volumes.empty())
m_volumes.check_outside_state(m_bed.build_volume(), &state); check_volumes_outside_state(m_bed.build_volume(), &state);
return state; return state;
} }
bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const
{
auto volume_below = [](GLVolume& volume) -> bool
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
auto volume_sinking = [](GLVolume& volume) -> bool
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
// Cached bounding box of a volume above the print bed.
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
// Cached 3D convex hull of a volume above the print bed.
auto volume_convex_mesh = [this, volume_sinking](GLVolume& volume) -> const TriangleMesh&
{ return volume_sinking(volume) ? m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
auto volumes_to_process_idxs = [this]() {
std::vector<unsigned int> ret;
if (m_selection.is_empty()) {
ret = std::vector<unsigned int>(m_volumes.volumes.size());
std::iota(ret.begin(), ret.end(), 0);
}
else {
const GUI::Selection::IndicesList& selected_volume_idxs = m_selection.get_volume_idxs();
ret.assign(selected_volume_idxs.begin(), selected_volume_idxs.end());
}
return ret;
};
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
bool contained_min_one = false;
const std::vector<unsigned int> volumes_idxs = volumes_to_process_idxs();
for (unsigned int vol_idx : volumes_idxs) {
GLVolume* volume = m_volumes.volumes[vol_idx];
if (!volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (!volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
BuildVolume::ObjectState state;
if (volume_below(*volume))
state = BuildVolume::ObjectState::Below;
else {
switch (build_volume.type()) {
case BuildVolume::Type::Rectangle:
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
state = build_volume.volume_state_bbox(volume_bbox(*volume));
break;
case BuildVolume::Type::Circle:
case BuildVolume::Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case BuildVolume::Type::Custom:
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
break;
default:
// Ignore, don't produce any collision.
state = BuildVolume::ObjectState::Inside;
break;
}
assert(state != BuildVolume::ObjectState::Below);
}
volume->is_outside = state != BuildVolume::ObjectState::Inside;
if (volume->printable) {
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
overall_state = ModelInstancePVS_Fully_Outside;
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
overall_state = ModelInstancePVS_Partly_Outside;
contained_min_one |= !volume->is_outside;
}
}
}
if (out_state != nullptr)
*out_state = overall_state;
return contained_min_one;
}
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
{ {
if (current_printer_technology() != ptSLA) if (current_printer_technology() != ptSLA)
@ -2462,7 +2593,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// checks for geometry outside the print volume to render it accordingly // checks for geometry outside the print volume to render it accordingly
if (!m_volumes.empty()) { if (!m_volumes.empty()) {
ModelInstanceEPrintVolumeState state; ModelInstanceEPrintVolumeState state;
const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state); const bool contained_min_one = check_volumes_outside_state(m_bed.build_volume(), &state);
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside); const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside); const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
@ -3429,17 +3560,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Not only detection of some modifiers !!! // Not only detection of some modifiers !!!
if (evt.Dragging()) { if (evt.Dragging()) {
GLGizmosManager::EType c = m_gizmos.get_current_type(); GLGizmosManager::EType c = m_gizmos.get_current_type();
if (current_printer_technology() == ptFFF &&
fff_print()->config().complete_objects){
if (c == GLGizmosManager::EType::Move || if (c == GLGizmosManager::EType::Move ||
c == GLGizmosManager::EType::Scale || c == GLGizmosManager::EType::Scale ||
c == GLGizmosManager::EType::Rotate ) c == GLGizmosManager::EType::Rotate) {
update_sequential_clearance();
} else {
if (c == GLGizmosManager::EType::Move ||
c == GLGizmosManager::EType::Scale ||
c == GLGizmosManager::EType::Rotate)
show_sinking_contours(); show_sinking_contours();
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
update_sequential_clearance(true);
} }
} }
else if (evt.LeftUp() && else if (evt.LeftUp() &&
@ -3639,7 +3765,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
trafo_type.set_relative(); trafo_type.set_relative();
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type); m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
update_sequential_clearance(); update_sequential_clearance(false);
wxGetApp().obj_manipul()->set_dirty(); wxGetApp().obj_manipul()->set_dirty();
m_dirty = true; m_dirty = true;
} }
@ -3945,7 +4071,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
if (wipe_tower_origin != Vec3d::Zero()) if (wipe_tower_origin != Vec3d::Zero())
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
reset_sequential_print_clearance(); if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
update_sequential_clearance(true);
m_sequential_print_clearance.m_evaluating = true;
}
m_dirty = true; m_dirty = true;
} }
@ -4030,6 +4159,11 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
if (!done.empty()) if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
update_sequential_clearance(true);
m_sequential_print_clearance.m_evaluating = true;
}
m_dirty = true; m_dirty = true;
} }
@ -4102,6 +4236,11 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
if (!done.empty()) if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
update_sequential_clearance(true);
m_sequential_print_clearance.m_evaluating = true;
}
m_dirty = true; m_dirty = true;
} }
@ -4369,16 +4508,33 @@ void GLCanvas3D::mouse_up_cleanup()
m_canvas->ReleaseMouse(); m_canvas->ReleaseMouse();
} }
void GLCanvas3D::update_sequential_clearance() void GLCanvas3D::update_sequential_clearance(bool force_contours_generation)
{ {
if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
return; return;
if (m_layers_editing.is_enabled() || m_gizmos.is_dragging()) if (m_layers_editing.is_enabled())
return; return;
auto instance_transform_from_volumes = [this](int object_idx, int instance_idx) {
for (const GLVolume* v : m_volumes.volumes) {
if (v->object_idx() == object_idx && v->instance_idx() == instance_idx)
return v->get_instance_transformation();
}
assert(false);
return Geometry::Transformation();
};
auto is_object_outside_printbed = [this](int object_idx) {
for (const GLVolume* v : m_volumes.volumes) {
if (v->object_idx() == object_idx && v->is_outside)
return true;
}
return false;
};
// collects instance transformations from volumes // collects instance transformations from volumes
// first define temporary cache // first: define temporary cache
unsigned int instances_count = 0; unsigned int instances_count = 0;
std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms; std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms;
for (size_t obj = 0; obj < m_model->objects.size(); ++obj) { for (size_t obj = 0; obj < m_model->objects.size(); ++obj) {
@ -4393,67 +4549,95 @@ void GLCanvas3D::update_sequential_clearance()
if (instances_count == 1) if (instances_count == 1)
return; return;
// second fill temporary cache with data from volumes // second: fill temporary cache with data from volumes
for (const GLVolume* v : m_volumes.volumes) { for (const GLVolume* v : m_volumes.volumes) {
if (v->is_modifier || v->is_wipe_tower) if (v->is_wipe_tower)
continue; continue;
auto& transform = instance_transforms[v->object_idx()][v->instance_idx()]; const int object_idx = v->object_idx();
const int instance_idx = v->instance_idx();
auto& transform = instance_transforms[object_idx][instance_idx];
if (!transform.has_value()) if (!transform.has_value())
transform = v->get_instance_transformation(); transform = instance_transform_from_volumes(object_idx, instance_idx);
} }
// helper function to calculate the transformation to be applied to the sequential print clearance contours
auto instance_trafo = [](const Transform3d& hull_trafo, const Geometry::Transformation& inst_trafo) {
Vec3d offset = inst_trafo.get_offset() - hull_trafo.translation();
offset.z() = 0.0;
return Geometry::translation_transform(offset) *
Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ());
};
// calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
// this is done only the first time this method is called while moving the mouse, // this is done only the first time this method is called while moving the mouse,
// the results are then cached for following displacements // the results are then cached for following displacements
if (m_sequential_print_clearance_first_displacement) { if (force_contours_generation || m_sequential_print_clearance_first_displacement) {
m_sequential_print_clearance.m_hull_2d_cache.clear(); m_sequential_print_clearance.m_evaluating = false;
m_sequential_print_clearance.m_hulls_2d_cache.clear();
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
const double mitter_limit = scale_(0.1); const double mitter_limit = scale_(0.1);
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); m_sequential_print_clearance.m_hulls_2d_cache.reserve(m_model->objects.size());
for (size_t i = 0; i < m_model->objects.size(); ++i) { for (size_t i = 0; i < m_model->objects.size(); ++i) {
ModelObject* model_object = m_model->objects[i]; ModelObject* model_object = m_model->objects[i];
ModelInstance* model_instance0 = model_object->instances.front(); Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0);
Geometry::Transformation trafo = model_instance0->get_transformation(); trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() });
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() }); Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first;
const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), if (is_object_outside_printbed((int)i))
continue;
Polygon hull_2d = model_object->convex_hull_2d(trafo.get_matrix());
if (!hull_2d.empty()) {
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
shrink_factor, const Polygons offset_res = offset(hull_2d, shrink_factor, jtRound, mitter_limit);
jtRound, mitter_limit).front(); if (!offset_res.empty())
hull_2d = offset_res.front();
}
Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); new_hull_2d.reserve(hull_2d.points.size());
cache_hull_2d.reserve(hull_2d.points.size());
const Transform3d inv_trafo = trafo.get_matrix().inverse(); const Transform3d inv_trafo = trafo.get_matrix().inverse();
for (const Point& p : hull_2d.points) { for (const Point& p : hull_2d.points) {
cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0)); new_hull_2d.emplace_back(Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
} }
} }
m_sequential_print_clearance_first_displacement = false;
}
// calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) ContoursList contours;
Polygons polygons; contours.contours.reserve(instance_transforms.size());
polygons.reserve(instances_count); contours.trafos = std::vector<std::pair<size_t, Transform3d>>();
(*contours.trafos).reserve(instances_count);
for (size_t i = 0; i < instance_transforms.size(); ++i) { for (size_t i = 0; i < instance_transforms.size(); ++i) {
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
Points hull_pts;
hull_pts.reserve(hull.size());
for (size_t j = 0; j < hull.size(); ++j) {
hull_pts.emplace_back(scaled<double>(hull[j].x()), scaled<double>(hull[j].y()));
}
contours.contours.emplace_back(Geometry::convex_hull(std::move(hull_pts)));
const auto& instances = instance_transforms[i]; const auto& instances = instance_transforms[i];
for (const auto& instance : instances) { for (const auto& instance : instances) {
const Transform3d& trafo = instance->get_matrix(); (*contours.trafos).emplace_back(i, instance_trafo(hull_trafo, *instance));
const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i];
Points inst_pts;
inst_pts.reserve(hull_2d.size());
for (size_t j = 0; j < hull_2d.size(); ++j) {
const Vec3d p = trafo * hull_2d[j];
inst_pts.emplace_back(scaled<double>(p.x()), scaled<double>(p.y()));
}
polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts)));
} }
} }
// sends instances 2d hulls to be rendered set_sequential_print_clearance_contours(contours, false);
set_sequential_print_clearance_visible(true); m_sequential_print_clearance_first_displacement = false;
set_sequential_print_clearance_render_fill(false); }
set_sequential_print_clearance_polygons(polygons); else {
if (!m_sequential_print_clearance.empty()) {
std::vector<Transform3d> trafos;
trafos.reserve(instances_count);
for (size_t i = 0; i < instance_transforms.size(); ++i) {
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
const auto& instances = instance_transforms[i];
for (const auto& instance : instances) {
trafos.emplace_back(instance_trafo(hull_trafo, *instance));
}
}
m_sequential_print_clearance.update_instances_trafos(trafos);
}
}
} }
bool GLCanvas3D::is_object_sinking(int object_idx) const bool GLCanvas3D::is_object_sinking(int object_idx) const
@ -5933,7 +6117,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
} }
} }
if (m_requires_check_outside_state) { if (m_requires_check_outside_state) {
m_volumes.check_outside_state(build_volume, nullptr); check_volumes_outside_state(build_volume, nullptr);
m_requires_check_outside_state = false; m_requires_check_outside_state = false;
} }
} }
@ -6028,15 +6212,20 @@ void GLCanvas3D::_render_selection()
void GLCanvas3D::_render_sequential_clearance() void GLCanvas3D::_render_sequential_clearance()
{ {
if (m_layers_editing.is_enabled() || m_gizmos.is_dragging()) if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
return;
if (m_layers_editing.is_enabled())
return; return;
switch (m_gizmos.get_current_type()) switch (m_gizmos.get_current_type())
{ {
case GLGizmosManager::EType::Flatten: case GLGizmosManager::EType::Flatten:
case GLGizmosManager::EType::Cut: case GLGizmosManager::EType::Cut:
case GLGizmosManager::EType::Hollow: case GLGizmosManager::EType::MmuSegmentation:
case GLGizmosManager::EType::SlaSupports: case GLGizmosManager::EType::Measure:
case GLGizmosManager::EType::Emboss:
case GLGizmosManager::EType::Simplify:
case GLGizmosManager::EType::FdmSupports: case GLGizmosManager::EType::FdmSupports:
case GLGizmosManager::EType::Seam: { return; } case GLGizmosManager::EType::Seam: { return; }
default: { break; } default: { break; }

View File

@ -618,23 +618,35 @@ public:
return ret; return ret;
} }
struct ContoursList
{
// list of unique contours
Polygons contours;
// if defined: list of transforms to apply to contours
std::optional<std::vector<std::pair<size_t, Transform3d>>> trafos;
bool empty() const { return contours.empty(); }
};
private: private:
void load_arrange_settings(); void load_arrange_settings();
class SequentialPrintClearance class SequentialPrintClearance
{ {
GLModel m_fill; GLModel m_fill;
GLModel m_perimeter; // list of unique contours
bool m_render_fill{ true }; std::vector<GLModel> m_contours;
bool m_visible{ false }; // list of transforms used to render the contours
std::vector<std::pair<size_t, Transform3d>> m_instances;
bool m_evaluating{ false };
std::vector<Pointf3s> m_hull_2d_cache; std::vector<std::pair<Pointf3s, Transform3d>> m_hulls_2d_cache;
public: public:
void set_polygons(const Polygons& polygons); void set_contours(const ContoursList& contours, bool generate_fill);
void set_render_fill(bool render_fill) { m_render_fill = render_fill; } void update_instances_trafos(const std::vector<Transform3d>& trafos);
void set_visible(bool visible) { m_visible = visible; }
void render(); void render();
bool empty() const { return m_contours.empty(); }
friend class GLCanvas3D; friend class GLCanvas3D;
}; };
@ -725,6 +737,9 @@ public:
const GLVolumeCollection& get_volumes() const { return m_volumes; } const GLVolumeCollection& get_volumes() const { return m_volumes; }
void reset_volumes(); void reset_volumes();
ModelInstanceEPrintVolumeState check_volumes_outside_state() const; ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
bool check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
void init_gcode_viewer() { m_gcode_viewer.init(); } void init_gcode_viewer() { m_gcode_viewer.init(); }
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
@ -958,24 +973,23 @@ public:
} }
void reset_sequential_print_clearance() { void reset_sequential_print_clearance() {
m_sequential_print_clearance.set_visible(false); m_sequential_print_clearance.m_evaluating = false;
m_sequential_print_clearance.set_render_fill(false); m_sequential_print_clearance.set_contours(ContoursList(), false);
m_sequential_print_clearance.set_polygons(Polygons());
} }
void set_sequential_print_clearance_visible(bool visible) { void set_sequential_print_clearance_contours(const ContoursList& contours, bool generate_fill) {
m_sequential_print_clearance.set_visible(visible); m_sequential_print_clearance.set_contours(contours, generate_fill);
} }
void set_sequential_print_clearance_render_fill(bool render_fill) { bool is_sequential_print_clearance_empty() const {
m_sequential_print_clearance.set_render_fill(render_fill); return m_sequential_print_clearance.empty();
} }
void set_sequential_print_clearance_polygons(const Polygons& polygons) { bool is_sequential_print_clearance_evaluating() const {
m_sequential_print_clearance.set_polygons(polygons); return m_sequential_print_clearance.m_evaluating;
} }
void update_sequential_clearance(); void update_sequential_clearance(bool force_contours_generation);
const Print* fff_print() const; const Print* fff_print() const;
const SLAPrint* sla_print() const; const SLAPrint* sla_print() const;

View File

@ -596,6 +596,38 @@ void GLModel::init_from(const indexed_triangle_set& its)
} }
} }
void GLModel::init_from(const Polygon& polygon, float z)
{
if (is_initialized()) {
// call reset() if you want to reuse this model
assert(false);
return;
}
Geometry& data = m_render_data.geometry;
data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 };
const size_t segments_count = polygon.points.size();
data.reserve_vertices(2 * segments_count);
data.reserve_indices(2 * segments_count);
// vertices + indices
unsigned int vertices_counter = 0;
for (size_t i = 0; i < segments_count; ++i) {
const Point& p0 = polygon.points[i];
const Point& p1 = (i == segments_count - 1) ? polygon.points.front() : polygon.points[i + 1];
data.add_vertex(Vec3f(unscale<float>(p0.x()), unscale<float>(p0.y()), z));
data.add_vertex(Vec3f(unscale<float>(p1.x()), unscale<float>(p1.y()), z));
vertices_counter += 2;
data.add_line(vertices_counter - 2, vertices_counter - 1);
}
// update bounding box
for (size_t i = 0; i < vertices_count(); ++i) {
m_bounding_box.merge(data.extract_position_3(i).cast<double>());
}
}
void GLModel::init_from(const Polygons& polygons, float z) void GLModel::init_from(const Polygons& polygons, float z)
{ {
if (is_initialized()) { if (is_initialized()) {

View File

@ -227,6 +227,7 @@ namespace GUI {
void init_from(const TriangleMesh& mesh); void init_from(const TriangleMesh& mesh);
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_SMOOTH_NORMALS
void init_from(const indexed_triangle_set& its); void init_from(const indexed_triangle_set& its);
void init_from(const Polygon& polygon, float z);
void init_from(const Polygons& polygons, float z); void init_from(const Polygons& polygons, float z);
bool init_from_file(const std::string& filename); bool init_from_file(const std::string& filename);

View File

@ -3294,9 +3294,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
// or hide the old one. // or hide the old one.
process_validation_warning(warning); process_validation_warning(warning);
if (printer_technology == ptFFF) { if (printer_technology == ptFFF) {
view3D->get_canvas3d()->reset_sequential_print_clearance(); GLCanvas3D* canvas = view3D->get_canvas3d();
view3D->get_canvas3d()->set_as_dirty(); if (canvas->is_sequential_print_clearance_evaluating()) {
view3D->get_canvas3d()->request_extra_frame(); canvas->reset_sequential_print_clearance();
canvas->set_as_dirty();
canvas->request_extra_frame();
}
} }
} }
else { else {
@ -3305,18 +3308,29 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
notification_manager->push_validate_error_notification(err); notification_manager->push_validate_error_notification(err);
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
if (printer_technology == ptFFF) { if (printer_technology == ptFFF) {
const Print* print = background_process.fff_print(); GLCanvas3D* canvas = view3D->get_canvas3d();
Polygons polygons; if (canvas->is_sequential_print_clearance_empty() || canvas->is_sequential_print_clearance_evaluating()) {
if (print->config().complete_objects) GLCanvas3D::ContoursList contours;
Print::sequential_print_horizontal_clearance_valid(*print, &polygons); contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
view3D->get_canvas3d()->set_sequential_print_clearance_visible(true); canvas->set_sequential_print_clearance_contours(contours, true);
view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(true); }
view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons);
} }
} }
} }
else { else {
if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) { if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) {
if (printer_technology == ptFFF) {
// Object manipulation with gizmos may end up in a null transformation.
// In this case, we need to trigger the completion of the sequential print clearance contours evaluation
GLCanvas3D* canvas = view3D->get_canvas3d();
if (canvas->is_sequential_print_clearance_evaluating()) {
GLCanvas3D::ContoursList contours;
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
canvas->set_sequential_print_clearance_contours(contours, true);
canvas->set_as_dirty();
canvas->request_extra_frame();
}
}
std::string warning; std::string warning;
std::string err = background_process.validate(&warning); std::string err = background_process.validate(&warning);
if (!err.empty()) if (!err.empty())
@ -4418,7 +4432,6 @@ void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)
void Plater::priv::on_3dcanvas_mouse_dragging_started(SimpleEvent&) void Plater::priv::on_3dcanvas_mouse_dragging_started(SimpleEvent&)
{ {
view3D->get_canvas3d()->reset_sequential_print_clearance();
} }
// Update the scene from the background processing, // Update the scene from the background processing,