Attempt to speed up outside bed detection (#8869)

* Don't check intersection if points below bed is also inside the bed, when the bed is convex

* Skip intersection check if bbox not overlapping

* Remove duplicated out of bed check

* Faster (but less accurate) bbox test

* Merge branch 'main' into dev/faster-outside-check

# Conflicts:
#	src/libslic3r/Model.cpp
This commit is contained in:
Noisyfox 2025-04-14 23:23:08 +08:00 committed by GitHub
parent b795708852
commit c90b3a2cce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 24 additions and 12 deletions

View File

@ -268,7 +268,7 @@ inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min)
template<class Tout, class Tin>
auto cast(const BoundingBoxBase<Tin> &b)
{
return BoundingBoxBase<Vec<3, Tout>>{b.min.template cast<Tout>(),
return BoundingBoxBase<Vec<2, Tout>>{b.min.template cast<Tout>(),
b.max.template cast<Tout>()};
}

View File

@ -188,7 +188,7 @@ static inline BuildVolume::ObjectState rectangle_test(const indexed_triangle_set
// Trim the input transformed triangle mesh with print bed and test the remaining vertices with is_inside callback.
// Return inside / colliding / outside state.
template<typename InsideFn>
BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, InsideFn is_inside)
BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, bool convex, InsideFn is_inside)
{
size_t num_inside = 0;
size_t num_above = 0;
@ -205,8 +205,10 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
const auto sign = [](const stl_vertex& pt) { return pt.z() > world_min_z ? 1 : pt.z() < world_min_z ? -1 : 0; };
bool below_outside = false;
for (const stl_vertex &v : its.vertices) {
const stl_vertex pt = trafo * v;
stl_vertex pt = trafo * v;
const int s = sign(pt);
sides.emplace_back(s);
if (s >= 0) {
@ -214,6 +216,10 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
++ num_above;
if (is_inside(pt))
++ num_inside;
} else if (convex && !below_outside) {
pt.z() = 0;
if (!is_inside(pt))
below_outside = true;
}
}
@ -225,7 +231,8 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
// 2) Calculate intersections of triangle edges with the build surface.
inside = num_inside > 0;
outside = num_inside < num_above;
if (num_above < its.vertices.size() && ! (inside && outside)) {
// Orca: for convex shape, if everything inside then don't bother check intersection
if (num_above < its.vertices.size() && !(inside && outside) && (!(inside && !below_outside) || !convex)) {
// Not completely above the build surface and status may still change by testing edges intersecting the build platform.
for (const stl_triangle_vertex_indices &tri : its.indices) {
const int s[3] = { sides[tri(0)], sides[tri(1)], sides[tri(2)] };
@ -284,21 +291,21 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& i
// The following test correctly interprets intersection of a non-convex object with a rectangular build volume.
//return rectangle_test(its, trafo, to_2d(build_volume.min), to_2d(build_volume.max), build_volume.max.z());
//FIXME This test does NOT correctly interprets intersection of a non-convex object with a rectangular build volume.
return object_state_templ(its, trafo, may_be_below_bed, [build_volumef](const Vec3f &pt) { return build_volumef.contains(pt); });
return object_state_templ(its, trafo, may_be_below_bed, true, [build_volumef](const Vec3f &pt) { return build_volumef.contains(pt); });
}
case BuildVolume_Type::Circle:
{
Geometry::Circlef circle { unscaled<float>(m_circle.center), unscaled<float>(m_circle.radius + SceneEpsilon) };
return m_max_print_height == 0.0 ?
object_state_templ(its, trafo, may_be_below_bed, [circle](const Vec3f &pt) { return circle.contains(to_2d(pt)); }) :
object_state_templ(its, trafo, may_be_below_bed, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
object_state_templ(its, trafo, may_be_below_bed, true, [circle](const Vec3f& pt) { return circle.contains(to_2d(pt)); }) :
object_state_templ(its, trafo, may_be_below_bed, true, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
}
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:
return m_max_print_height == 0.0 ?
object_state_templ(its, trafo, may_be_below_bed, [this](const Vec3f &pt) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); }) :
object_state_templ(its, trafo, may_be_below_bed, [this, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); });
object_state_templ(its, trafo, may_be_below_bed, m_type == BuildVolume_Type::Convex, [this](const Vec3f &pt) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); }) :
object_state_templ(its, trafo, may_be_below_bed, m_type == BuildVolume_Type::Convex, [this, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); });
case BuildVolume_Type::Invalid:
default:
return ObjectState::Inside;

View File

@ -3317,7 +3317,14 @@ ModelInstanceEPrintVolumeState ModelInstance::calc_print_volume_state(const Buil
}
const Transform3d matrix = this->get_matrix() * vol->get_matrix();
BuildVolume::ObjectState state = build_volume.object_state(vol->mesh().its, matrix.cast<float>(), true /* may be below print bed */);
const auto bboxt = bb.transformed(matrix);
const BoundingBoxf bbox2d{to_2d(bboxt.min), to_2d(bboxt.max)};
BuildVolume::ObjectState state;
if (!build_volume.bounding_volume2d().inflated(BuildVolume::SceneEpsilon).overlap(bbox2d))
state = BuildVolume::ObjectState::Outside;
else
state = build_volume.object_state(vol->mesh().its, matrix.cast<float>(), true /* may be below print bed */);
if (state == BuildVolume::ObjectState::Inside)
// Volume is completely inside.
inside_outside |= INSIDE;

View File

@ -5286,8 +5286,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": enter, force_validation=%1% postpone_error_messages=%2%, switch_print=%3%, was_running=%4%")%force_validation %postpone_error_messages %switch_print %was_running;
if (switch_print)
{
// Update the "out of print bed" state of ModelInstances.
this->update_print_volume_state();
//BBS: update the current print to the current plate
this->partplate_list.update_slice_context_to_current_plate(background_process);
this->preview->update_gcode_result(partplate_list.get_current_slice_result());